Нужно ли синхронизировать методы внутри метода run в Java при вызове метода start? - PullRequest
0 голосов
/ 02 декабря 2011

Может кто-нибудь сказать мне, прав я или нет?У меня есть два потока, которые будут работать параллельно.

class MyThread extends Thread {

    MyThread() {
    }

    method1() {
    }

    method2() {
    }

    method3() {
    }

    approach(1):

        run() {
            method1();
            method2();
            method3();
        }

    approach(2):

        run() {
            //the code of method1 is here (no method calling)
            //the code of method2 is here (no method calling)
            //the code of method3 is here (no method calling)
        }

}

class Test{
    public static void main(){
        Thread t1 = new Thread();
        t1.start();
        Thread t2 = new Thread();
        t2.start();
    }
}

method1, method2 и method3 не имеют доступа к глобальным общим данным, но их коды выполняют некоторую запись в локальной переменной в разделе методаТаким образом, я думаю, я не могу разрешить выполнение перекрытия в разделе метода.

Тем самым: в approach(1): мне нужно синхронизировать методы (method1, method2 и method3), верно?

в approach(2): нет необходимостисинхронизировать фрагменты кода, верно?

Если я прав в обоих подходах, использование approach(2) даст лучшую производительность, верно?

Ответы [ 6 ]

3 голосов
/ 02 декабря 2011

Краткий ответ: вам не нужна синхронизация. Оба подхода эквивалентны с точки зрения безопасности потока.

Более длинный ответ:

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

  1. гарантирует, что если поток A находится внутри блока, который синхронизирован с объектом M, никакой другой поток не может войти в блок, синхронизированный с тем же объектом M, пока поток A не завершит работу со своим блоком кода
  2. проверяет, что если поток A выполнил работу в блоке, который синхронизирует объект M, а затем завершает этот блок, а затем поток B входит в блок, который также синхронизируется M, тогда поток B увидит все, что поток A сделал в своем синхронизированном блоке. Это называется установлением отношения «происходит до того».

Обратите внимание, что синхронизированный метод - это просто сокращение для переноса кода метода в синхронизированный (this) {...}.

В дополнение к этим двум вещам модель памяти Java (JMM) гарантирует, что в одном потоке все будет происходить так, как если бы они не были переупорядочены. (Они могут быть переупорядочены по разным причинам, включая эффективность - но не так, как ваша программа может заметить в одном потоке. Например, если вы выполните «x = 1; y = 2», компилятор может свободно переключаться что такое, что y = 2 происходит до x = 1, так как один поток не может заметить разницу. Если несколько потоков обращаются к x и y, то очень возможно, без надлежащей синхронизации, для другого потока увидеть y = 2 прежде чем он увидит х = 1.)

Итак, возвращаясь к вашему первоначальному вопросу, есть пара интересных заметок.

Во-первых, поскольку синхронизированный метод является сокращением для помещения всего метода в блок "synchronized (this) {...}", методы t1 и методы t2 не будут синхронизироваться с одной и той же ссылкой и, следовательно, не будут синхронизированы относительно друг друга. Методы t1 будут синхронизироваться только с объектом t1, а методы t2 будут синхронизироваться только с t2. Другими словами, было бы прекрасно для одновременной работы t1.method1 () и t2.method1 (). Итак, из этих двух вещей, которые предоставляет ключевое слово synchronized, первое (исключительность входа в блок) не имеет значения. Все может пойти примерно так:

  1. t1 хочет ввести метод1. Он должен получить монитор t1, который не оспаривается, поэтому он получает его и входит в блок
  2. t2. хочет ввести метод2. Ему нужно получить 11 мониторов, который не оспаривается - он получает его и входит в блок
  3. t1 завершает method1 и освобождает удержание на мониторе t1
  4. t2 завершает method1 и отпускает удержание на мониторе t2

Что касается второй вещи, которую выполняет синхронизация (установление происходит до), синхронизация method1 () и method2 () будет в основном гарантировать, что t1.method1 () произойдет до t1.method2 (). Но поскольку оба эти события в любом случае происходят в одном потоке (поток t1), JMM в любом случае гарантирует, что это произойдет.

Так что на самом деле все становится еще немного ужаснее. Если бы t1 и t2 имели общее состояние - то есть, была бы необходима синхронизация - тогда было бы достаточно сделать синхронизированные методы , а не . Помните, что синхронизированный метод означает синхронизированный (this) {...}, поэтому методы t1 будут синхронизированы с t1, а методы t2 будут с t2. На самом деле вы бы не установили никаких отношений «происходит до» между методами t1 и t2.

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

Предположим, что t1 и t2 оба знают об одной и той же ссылке, LOCK. Оба имеют такие методы, как:

method1() {
    synchronized(LOCK) {
        // do whatever
    }
}

Теперь все может пойти примерно так:

  1. t1 хочет ввести метод1. Он должен получить монитор LOCK, который не оспаривается, поэтому он получает его и входит в блок
  2. t2 хочет ввести метод1. Он должен получить монитор LOCK, который уже удерживается t1, поэтому t2 удерживается.
  3. t1 завершает method1 и освобождает удержание на мониторе LOCK
  4. t2 теперь может получить монитор LOCK, поэтому он делает это и запускается по методу метода 1
  5. t2 завершает method1 и отпускает удержание на мониторе LOCK
1 голос
/ 02 декабря 2011

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

С такого рода проблемой сталкиваются в случае статических / классовых переменных.Если несколько потоков пытаются изменить значение статических переменных одновременно, возникает проблема, поэтому нам необходимо выполнить синхронизацию.

0 голосов
/ 02 декабря 2011

method1, 2 и 3 не будут выполняться одновременно, поэтому, если переменные, которые они читают / пишут, не передаются за пределы класса другим потокам во время их работы, тогда синхронизация не требуется и нет необходимости встроенного.

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

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

Если ожидается, что другие потоки будут читать данные, измененныеmethod1, 2 или 3, затем необходимо синхронизировать метод выполнения (или их в синхронизированном блоке), чтобы настроить шлюз так, чтобы JVM установила барьер памяти и гарантировала, что другие потокиможно увидеть данные после выполнения m1,2 и 3.

0 голосов
/ 02 декабря 2011

Тем самым: в подходе (1): мне нужно синхронизировать методы (method1, method2 и method3), верно?в подходе (2): нет необходимости синхронизировать фрагменты кода, верно?

Вызов встроенных методов v / s, вызывающий несколько методов, не определяет, должен ли метод быть синхронизирован или нет.Я бы порекомендовал вам прочитать это , а затем попросить больше разъяснений.

Если я прав в обоих подходах, использование подхода (2) даст лучшую производительность,верно?

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

0 голосов
/ 02 декабря 2011

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

У вас не будет никаких улучшений в скорости между двумя подходами, это просто лучшая организация кода (более короткие методы легче понять)

Если каждый метод не зависит от другого, вы можете подумать, относятся ли они к одному и тому же классу.Если вы хотите получить прирост производительности, создайте 3 разных класса и выполните несколько потоков для каждого метода (прирост производительности зависит от количества доступных ядер, процессоров / ресурсов и т. Д.)

0 голосов
/ 02 декабря 2011

Если методы, которые вы вызываете, не пишут в глобальные общие данные, вам не нужно синхронизировать их.

В многопоточной программе каждый поток имеет свой собственный стек вызовов. Локальные переменные каждого метода будут отдельными в каждом потоке и не будут перезаписывать друг друга.

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

...