0
点赞
收藏
分享

微信扫一扫

变量的线程安全分析

骨灰级搬砖工 2022-02-21 阅读 117

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

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

局部变量是否线程安全?

局部变量是线程安全的

但局部变量引用的对象则未必

如果该对象没有逃离方法的作用范围,它是线程安全的

如果该对象逃离方法的作用范围,需要考虑线程安全

局部变量线程安全分析

想一想,这段代码被多个线程访问,它是线程安全的吗?

    public static void test1() {
        int i = 10;
        i++;
    }

i++不是一个原子操作,它是线程不安全的,也许会从这方面去考虑,但实际上这段代码是线程安全的。

反编译为字节码后,一共有4行指令,第一行指令准备了一个常量10 第二行指令把10赋值给i,第三行指令的时候在局部变量i上进行自增1的操作,第四行指令就return了。

从这几行代码来看, 发现局部变量的i++操作和静态变量的i++的字节码指令是稍有不同的。incc这个指令只有一行就完成了++操作,就不会像静态变量的i++一样指令被分成了多个,影响原子性。

而每个线程调用此方法时,局部变量i会在每个线程的栈帧内存中被创建多份,因此不存在共享。

还记得局部变量是线程私有的吗?

线程分别在自己的栈帧中完成了i++操作,互不影响。

局部变量引用

在循环里调用method1,method2,method3,让mothod2在list里加一个元素,再让method3去remove这个元素。

调用执行后很快的就报错了。

public class TestThreadSafe {

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

    private void method2() {
        list.add("1");
    }

    private void method3() {
        list.remove(0);
    }
}
}

因为method2和methoed3都调用的同一个对象中的list成员变量。

 进行一些小改动,让它变得线程安全,让list成为方法内的局部变量。

class ThreadSafe {
    public final 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");
    }

    private void method3(ArrayList<String> list) {
        System.out.println(1);
        list.remove(0);
    }
}

分析这段代码为什么变成了线程安全的?

  • list是局部变量,每个线程调用时会创建其不同的实例,没有共享。
  • 而method2的参数是method1中传递过来的,与method1引用同一个对象。
  • method3的参数分析与method2相同。

 

举报

相关推荐

0 条评论