Поток имеет свою собственную копию данных? - PullRequest
0 голосов
/ 15 апреля 2020

Я где-то читал, что каждый поток имеет свою собственную копию общих состояний, даже если я использую синхронизированные или блокировки для изменения переменных, что гарантирует, что измененное состояние будет сброшено в основную память, а не в собственную нить кэш-память.

Я знаю изменчивые гарантии и оправдывает вышеприведенный сценарий, даже я знаю, что синхронизированные тоже оправдывает.

Каким образом синхронизированные гарантируют, что изменение значения происходит в основной памяти, а не в потоке кеша память.

Ex Thread 1

synchronized(this)
{
int a = 0;
a = 5;
}  ----> the value might got changed in thread's cache memory another thread entering the block could read a value as 0

volatilte int a = 0;
a = 5; ----> another executing thread will read a values as 5

Ответы [ 2 ]

3 голосов
/ 16 апреля 2020

Вы должны рассуждать в терминах happens-before, думая о многопоточном коде в java, это то, что использует JLS, и это то, что вам следует использовать. Точка.

Возвращаясь к вашему примеру, вы жуете volatile и synchronized вместе, как будто они делают одно и то же, вроде как - нет. Даже ваш пример не работает, чтобы «другой» поток видел гарантированную a = 5, он должен синхронизироваться с такой же блокировкой , чего у вас нет. Тест jcstress доказывает, что вы не правы (я дам вам понять, как именно это запустить)

@JCStressTest
@State
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "whatever")
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "whatever")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "whatever")
public class DifferentSynchronizedObjects {

    int x, y;
    private Object lock = new Object();

    @Actor
    public void actor1() {
        synchronized (lock) {
            x = 1;
            y = 1;
        }
    }

    @Actor
    public void actor2(II_Result r) {
        r.r1 = x;
        r.r2 = y;
    }
} 

Даже если вы не понимаете код, его главная «точка продажи» такова:

@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")

Вы можете прочитать это как: "пока вы были в синхронизированном состоянии (блокировка) {....}, какой-то другой поток пришел и прочитал x и y". Этот поток чтения видел 1, 0 (x = 1, y = 0) и теперь подумайте об этом. Вы были в блоке synchronized, почему какой-то поток прочитал x = 1 и y = 0, вы не "защищены"? Нет, ты не. Если вы запустите это - вы получите 1, 0.

РЕДАКТИРОВАТЬ, чтобы ответить на комментарий

Вы думаете вы защищаете обе записи в x и y - но так как JLS не дает таких гарантий, это ваше понимание вещей, что неправильно. Так просто. Единственная защита, которую вы на самом деле получаете, - это если ваш писатель и читатель будут использовать одну и ту же блокировку.

Оптимизатор может «увидеть», что вы используете это lock только внутри метода, и, таким образом, преобразовать этот код в (по крайней мере, в теории):

@Actor
public void actor1() {

    Object lock = new Object(); // < --  make lock local       

    synchronized (lock) {
        x = 1;
        y = 1;
    }
}

Поскольку lock теперь локально для метода, какой смысл вообще его иметь? Никто не может получить к нему доступ, и просто полностью исключить его. Таким образом, вы получаете полностью незащищенный код, который выполняет две независимые записи.

Вывод таков: вы не следуете правилам, которые дает вам JLS, - будьте готовы к странным результатам.

1 голос
/ 15 апреля 2020

Без использования ключевого слова synchronized (или volatile) нет гарантии, что когда один поток изменяет значение переменной, используемой совместно с другими потоками, другие потоки могут видеть измененное значение. Нет никаких гарантий относительно того, когда переменная, хранящаяся в регистре ЦП одним потоком, «фиксируется» в основной памяти, и нет гарантии того, когда другие потоки «переопределят» переменную, хранящуюся в регистре ЦП, из основной памяти.

С ключевым словом synchronized, когда поток входит в блок syn c, он будет обновлять sh значения всех переменных, видимых для потока (в том же замке / объекте). Когда поток выходит из синхронизированного блока, все изменения переменных, видимых для потока и для той же блокировки, будут переданы / обновлены в основную память. Это на самом деле так же, как volatile работает.

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