Java并发(十七)----变量的线程安全分析
2023-12-15 04:36:31
1、成员变量和静态变量是否线程安全
-
如果它们没有共享,则线程安全
-
如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
-
如果只有读操作,则线程安全
-
如果有读写操作,则这段代码是临界区,需要考虑线程安全
-
2、局部变量是否线程安全
-
局部变量是线程安全的
-
但局部变量引用的对象则未必
-
如果该对象没有逃离方法的作用访问,它是线程安全的
-
如果该对象逃离方法的作用范围,需要考虑线程安全
-
3、局部变量线程安全分析
public static void test1() {
? ?int i = 10;
? ?i++;
}
每个线程调用 test1() 方法时局部变量 i,会在每个线程的栈帧内存中被创建多份,因此不存在共享
public static void test1();
? ?descriptor: ()V
? ?flags: ACC_PUBLIC, ACC_STATIC
? ?Code:
? ? ?stack=1, locals=1, args_size=0
? ? ? ? 0: bipush ? ? ? ?10
? ? ? ? 2: istore_0
? ? ? ? 3: iinc ? ? ? ? ?0, 1
? ? ? ? 6: return
? ? ?LineNumberTable:
? ? ? ?line 10: 0
? ? ? ?line 11: 3
? ? ? ?line 12: 6
? ? ?LocalVariableTable:
? ? ? ?Start ?Length ?Slot ?Name ? Signature
? ? ? ? ? ?3 ? ? ? 4 ? ? 0 ? ? i ? I
如图
局部变量的引用稍有不同,先看一个成员变量的例子
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"); ?// 访问的同一个成员变量list
? }
?
? ?private void method3() {
? ? ? ?list.remove(0);// 访问的同一个成员变量list
? }
}
执行
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();
? }
}
多运行几次就会发现,其中一种情况是,如果线程2 还未 add,线程1 remove 就会报错:
Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.remove(ArrayList.java:496)
at cn.itcast.n6.ThreadUnsafe.method3(TestThreadSafe.java:35)
at cn.itcast.n6.ThreadUnsafe.method1(TestThreadSafe.java:26)
at cn.itcast.n6.TestThreadSafe.lambda$main$0(TestThreadSafe.java:14)
at java.lang.Thread.run(Thread.java:748)
分析:
-
无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量
-
method3 与 method2 分析相同
将 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);
? ? ? }
? }
?
? ?private void method2(ArrayList<String> list) {
? ? ? ?list.add("1");
? }
?
? ?private void method3(ArrayList<String> list) {
? ? ? ?list.remove(0);
? }
}
那么就不会有上述问题了
分析:
-
list 是局部变量,每个线程调用时会创建其不同实例,没有共享
-
而 method2 的参数是从 method1 中传递过来的,与 method1 中引用同一个对象
-
method3 的参数分析与 method2 相同
方法访问修饰符带来的思考?
如果把 method2 和 method3 的方法修改为 public 会不会带来线程安全问题?
-
情况1:有其它线程调用 method2 和 method3
-
情况2:在 情况1 的基础上,为 ThreadSafe 类添加子类,子类覆盖 method2 或 method3 方法,即
class ThreadSafe {
? ?public final void method1(int loopNumber) {
? ? ? ?ArrayList<String> list = new ArrayList<>();
? ? ? ?for (int i = 0; i < loopNumber; i++) {
? ? ? ? ? ?method2(list);
? ? ? ? ? ?method3(list);
? ? ? }
? }
?
? ?private void method2(ArrayList<String> list) {
? ? ? ?list.add("1");
? }
?
? ?private void method3(ArrayList<String> list) {
? ? ? ?list.remove(0);
? }
}
?
class ThreadSafeSubClass extends ThreadSafe{
? ?@Override
? ?public void method3(ArrayList<String> list) {
? ? ? ?new Thread(() -> {
? ? ? ? ? ?list.remove(0);
? ? ? }).start();
? }
}
这样的话就会存在线程安全的问题。因为method3新开了一个线程,造成多个线程访问同一个共享资源,就会存在线程安全的问题。
从这个例子就可以看出 private 或 final 提供【安全】的意义所在。
文章来源:https://blog.csdn.net/FaithWh/article/details/134915122
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!