Безопасный код Java Thread - PullRequest
0 голосов
/ 15 мая 2018

Я видел код ниже в Интернете, и он говорит, что "код не является потокобезопасным". Я не мог понять, почему? Поскольку каждый поток, приведенный ниже, запускает 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;
    }
}

Ответы [ 3 ]

0 голосов
/ 15 мая 2018

У вас есть изменяемое состояние (переменная-член list), которое совместно используется двумя потоками.

Доступ к двум потокам:

if (list == null) {

и изменить:

        list = new ArrayList<>();

общее изменяемое состояние с помощью метода getlist, который никак не синхронизируется.

Следовательно, этот код не является потокобезопасным. Доступ и изменение общего изменяемого состояния без синхронизации - это плохая идея в целом.

0 голосов
/ 15 мая 2018

Этот код небезопасен по двум причинам, обе из которых основаны на следующих утверждениях:

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

Первая проблема - это состояние гонки. В системе с несколькими ядрами существует небольшая вероятность того, что два потока будут читать list в одно и то же время, оба потока увидят null, и два объекта ArrayList будут созданы и возвращены. А в одноядерной системе существует еще меньшая вероятность того, что один поток будет заменен другим сразу после чтения list, и вы получите тот же результат.

Но есть более коварная проблема, связанная с моделью памяти Java. Тот факт, что синхронизация отсутствует, означает, что между 1011 * записывающим list и другим (впоследствии) считывающим его потоком не существует отношения произойдет до . Это означает, что интерпретатор Java / JIT-компилятор не обязаны вставлять барьер памяти последовательности, чтобы гарантировать, что значение, записанное более ранним потоком, будет видимым для более позднего потока. Так что даже в случае, когда два потока работают в разное время ... более поздний поток может не видеть ненулевое значение, записанное более ранним потоком из-за различных эффектов кэширования памяти.

Решением обеих проблем является правильная синхронизация; например как это:

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

или объявив метод равным synchronized.

0 голосов
/ 15 мая 2018

Эта строка делает программу не поточно-безопасной

if (list == null) {

Оба потока могут одновременно видеть, что список пуст, и попытаться присвоить им новый массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...