变量的线程安全分析

本文探讨了线程安全问题,焦点在于成员变量和静态变量在多线程环境下的行为,以及局部变量如何确保或需考虑线程安全。通过实例和字节码分析,讲解了i++操作的线程安全特性,以及如何通过改变变量作用域来提升代码的线程安全性。
摘要由CSDN通过智能技术生成

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

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

局部变量是否线程安全?

局部变量是线程安全的

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

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

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

局部变量线程安全分析

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

    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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值