简体   繁体   English

Java线程安全代码

[英]Java Thread safe code

I saw the code below on the web, and it says "the code is not thread safe". 我在网上看到下面的代码,上面写着“代码不是线程安全的”。 I could not understand why? 我不明白为什么? Since, each thread below runs the getList will not let any other one to reach to the getList() . 因为,下面的每个线程运行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;
    }
}

This line makes the program not thread-safe 这行代码使程序不是线程安全的

if (list == null) {

Both threads might see the list is null at the same time and try to assign them new array. 两个线程可能同时看到列表为空,并尝试为其分配新数组。

You have mutable state (the member variable list ) that is shared between the two threads. 您具有两个线程之间共享的可变状态(成员变量list )。

The two threads can access: 这两个线程可以访问:

if (list == null) {

and modify: 并修改:

        list = new ArrayList<>();

the shared mutable state through the getlist method, which is not synchronized in any way. 通过getlist方法共享的可变状态,该状态不会以任何方式同步。

Therefore, this code is not thread safe. 因此,此代码不是线程安全的。 Accessing and modifying shared mutable state without synchronization is a bad idea in general. 通常,在没有同步的情况下访问和修改共享的可变状态是一个坏主意。

This code is unsafe for two reasons, both centering on these statements: 这段代码不安全有两个原因,它们都集中在以下语句上:

if (list == null) {
    list = new ArrayList<>();
}
return list;

First problem is that there is a race condition. 第一个问题是存在比赛条件。 On a system with multiple cores, there is a tiny probability that two threads will read list at exactly the same time, both threads will see null , and two ArrayList objects will be created and returned. 在具有多个内核的系统上,极有可能两个线程将完全同时读取list ,两个线程都将看到null ,并且将创建并返回两个ArrayList对象。 And on a single core system, there is an even smaller probability that one thread will be preempted by the other immediately after reading list and you will get the same result. 和单核系统上,还有一个更小的概率一个线程将被其他阅读后立即抢占list ,你会得到相同的结果。

But there is a more insidious problem that related to the Java Memory Model. 但是,还有一个与Java内存模型有关的更隐蔽的问题。 The fact that there is no synchronization means that there is no happens-before relationship between one thread writing list and another thread (subsequently) reading it. 没有同步的事实意味着一个线程写入list与另一个线程(随后)(随后)读取list之间不存在事前关系。 That means that the Java interpreter / JIT compiler are not obliged to insert memory barrier sequences to ensure that the value written by the earlier thread is visible to the later thread. 这意味着Java解释器/ JIT编译器没有义务插入内存屏障序列以确保由较早线程写入的值对较后线程可见。 So even in the case where the two threads run at different times ... the later thread may not see the non-null value written by the earlier thread due to various memory caching effects. 因此,即使在两个线程运行时间不同的情况下,由于各种内存缓存效果,后一个线程也可能看不到前一个线程写入的非空值。

The solution to both problems is to synchronize properly; 解决这两个问题的方法是正确同步。 eg like this: 例如这样:

synchronized (this) {
    if (list == null) {
        list = new ArrayList<>();
    }
    return list;
}

or by declaring the method to be synchronized . 或通过声明要synchronized的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM