[英]Java Thread safe code
我在网上看到下面的代码,上面写着“代码不是线程安全的”。 我不明白为什么? 因为,下面的每个线程运行getList都不会让其他任何线程到达getList()
。
public class MyClass {
private List<String> list;
public static void main (String[] args) throws InterruptedException {
MyClass obj = new MyClass();
Thread thread1 = new Thread(() -> {
System.out.println("thread1 : " + System.identityHashCode(obj.getList()));
});
Thread thread2 = new Thread(() -> {
System.out.println("thread2 : " + System.identityHashCode(obj.getList()));
});
thread1.start();
thread2.start();
}
private List<String> getList () {
if (list == null) {
list = new ArrayList<>();
}
return list;
}
}
这行代码使程序不是线程安全的
if (list == null) {
两个线程可能同时看到列表为空,并尝试为其分配新数组。
您具有两个线程之间共享的可变状态(成员变量list
)。
这两个线程可以访问:
if (list == null) {
并修改:
list = new ArrayList<>();
通过getlist
方法共享的可变状态,该状态不会以任何方式同步。
因此,此代码不是线程安全的。 通常,在没有同步的情况下访问和修改共享的可变状态是一个坏主意。
这段代码不安全有两个原因,它们都集中在以下语句上:
if (list == null) {
list = new ArrayList<>();
}
return list;
第一个问题是存在比赛条件。 在具有多个内核的系统上,极有可能两个线程将完全同时读取list
,两个线程都将看到null
,并且将创建并返回两个ArrayList
对象。 和单核系统上,还有一个更小的概率一个线程将被其他阅读后立即抢占list
,你会得到相同的结果。
但是,还有一个与Java内存模型有关的更隐蔽的问题。 没有同步的事实意味着一个线程写入list
与另一个线程(随后)(随后)读取list
之间不存在事前关系。 这意味着Java解释器/ JIT编译器没有义务插入内存屏障序列以确保由较早线程写入的值对较后线程可见。 因此,即使在两个线程运行时间不同的情况下,由于各种内存缓存效果,后一个线程也可能看不到前一个线程写入的非空值。
解决这两个问题的方法是正确同步。 例如这样:
synchronized (this) {
if (list == null) {
list = new ArrayList<>();
}
return list;
}
或通过声明要synchronized
的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.