【多线程: 变量的线程安全分析】

简介: 【多线程: 变量的线程安全分析】

【多线程: 变量的线程安全分析】

01.介绍

成员变量和静态变量是否线程安全?

如果它们没有共享,则线程安全

如果它们被共享了,根据它们的状态是否能够改变,又分两种情况 如果只有读操作,则线程安全 如果有读写操作,则这段代码是临界区,需要考虑线程安全

局部变量是否线程安全?

局部变量是线程安全的 但局部变量引用的对象则未必 如果该对象没有逃离方法的作用访问,它是线程安全的 如果该对象逃离方法的作用范围,需要考虑线程安全

02.成员变量线程安全分析

例子

import java.util.ArrayList;
class ThreadUnsafe {
    ArrayList<String> list = new ArrayList<>();
    public void method1(int loopNumber) {
        for (int i = 0; i < loopNumber; i++) {
            // { 临界区, 会产生竞态条件
            method2();
            method3();
// } 临界区
        }
    }
    public void method2() {
        list.add("1");
    }
    public void method3() {
        list.remove(0);
    }
}
public class TestXCAQ {
    static final int THREAD_NUMBER = 2;
    static final int LOOP_NUMBER = 200;
    public static void main(String[] args) {
        ThreadUnsafe test = new ThreadUnsafe();
        for (int i = 0; i < THREAD_NUMBER; i++) {
            new Thread(() -> {
                test.method1(LOOP_NUMBER);
            }, "Thread" + i).start();
        }
    }

}

结果

报错

解释

成员变量与静态变量都是被多个线程共享,一旦发送读写操作 就会出现线程安全问题,这里很明显method1调用了method2与method3 题目分别为添加一个字符串和删除一个字符串 是读写操作 所以出现了线程安全的问题,即还没有添加就先删除了。

图片解释

03.局部变量线程安全分析

例子

import java.util.ArrayList;

class ThreadUnsafe {
    public void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            // { 临界区, 会产生竞态条件
            method2(list);
            method3(list);
// } 临界区
        }
    }

    public void method2(ArrayList<String> list) {
        list.add("1");
    }
    public void method3(ArrayList<String> list) {
        list.remove(0);
    }
}
public class TestXCAQ {
    static final int THREAD_NUMBER = 2;
    static final int LOOP_NUMBER = 200;
    public static void main(String[] args) {
        ThreadUnsafe test = new ThreadUnsafe();
        for (int i = 0; i < THREAD_NUMBER; i++) {
            new Thread(() -> {
                test.method1(LOOP_NUMBER);
            }, "Thread" + i).start();
        }
    }

}

结果

结果为空

解释

我们这里把==list==有成员变量改为了局部变量 可以发现现在的结果为空 说明没有问题,原因是:局部变量每次创建都会复制出一个副本也就是线程1的list与线程2的list不是一个对象,所以不会出现共享同一个对象的问题 所以线程安全。

图片解释

局部变量线程不安全的情况

例子

import java.util.ArrayList;
class ThreadUnsafe {
    public void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            // { 临界区, 会产生竞态条件
            method2(list);
            method3(list);
// } 临界区
        }
    }

    public void method2(ArrayList<String> list) {
        list.add("1");
    }
    public void method3(ArrayList<String> list) {
        list.remove(0);
    }
}
class ThreadA extends ThreadUnsafe{
    @Override
    public void method2(ArrayList<String> list) {
        new Thread(()->{
            list.add("1");
        });
    }
}
public class TestXCAQ {
    static final int THREAD_NUMBER = 2;
    static final int LOOP_NUMBER = 200;
    public static void main(String[] args) {
        ThreadA test = new ThreadA();
        for (int i = 0; i < THREAD_NUMBER; i++) {
            new Thread(() -> {
                test.method1(LOOP_NUMBER);
            }, "Thread" + i).start();
        }
    }

}

结果

报错

解释

我们创建了ThreadUnsafe的子类ThreadA 并且重写了Thread2方法 把方法改为创建一个线程 此线程负责add,这样我们看结果 发现出现了错误,原因是 此时method2方法里的线程 与 调用method1的线程 现在共享一个对象list 导致线程不安全。

如何解决

1.使用final修饰父类 使其不能被继承

2.使用final修饰父类的方法 使其不能被子类重写

3.使用private修饰父类方法 使其不能被子类重写

目录
相关文章
|
3天前
|
监控 安全 Java
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
|
3天前
|
安全 算法 关系型数据库
线程安全--深入探究线程等待机制和死锁问题
线程安全--深入探究线程等待机制和死锁问题
|
3天前
|
缓存 安全 Java
多线程--深入探究多线程的重点,难点以及常考点线程安全问题
多线程--深入探究多线程的重点,难点以及常考点线程安全问题
|
3天前
|
数据采集 安全 Java
Python的多线程,守护线程,线程安全
Python的多线程,守护线程,线程安全
|
7天前
|
监控 Java 调度
Java多线程实战-从零手搓一个简易线程池(四)线程池生命周期状态流转实现
Java多线程实战-从零手搓一个简易线程池(四)线程池生命周期状态流转实现
|
7天前
|
设计模式 Java
Java多线程实战-从零手搓一个简易线程池(三)线程工厂,核心线程与非核心线程逻辑实现
Java多线程实战-从零手搓一个简易线程池(三)线程工厂,核心线程与非核心线程逻辑实现
|
7天前
|
Java 测试技术
Java多线程实战-从零手搓一个简易线程池(二)线程池实现与拒绝策略接口定义
Java多线程实战-从零手搓一个简易线程池(二)线程池实现与拒绝策略接口定义
|
7天前
|
存储 安全 Java
Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列
Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列
|
7天前
|
存储 消息中间件 Java
Java多线程实战-异步操作日志记录解决方案(AOP+注解+多线程)
Java多线程实战-异步操作日志记录解决方案(AOP+注解+多线程)
|
7天前
|
缓存 Java 测试技术
Java多线程实战-实现多线程文件下载,支持断点续传、日志记录等功能
Java多线程实战-实现多线程文件下载,支持断点续传、日志记录等功能