Следует ли синхронизировать метод запуска? Почему или почему нет?

Я всегда думал, что синхронизация метода run в classе java, который реализует Runnable, избыточна. Я пытаюсь понять, почему люди делают это:

public class ThreadedClass implements Runnable{ //other stuff public synchronized void run(){ while(true) //do some stuff in a thread } } } 

Кажется излишним и ненужным, так как они получают блокировку объекта для другого streamа. Вернее, они делают явным, что только один stream имеет доступ к методу run (). Но так как его метод запуска, разве это не его собственный stream? Следовательно, только он может получить доступ к нему, и ему не нужен отдельный механизм блокировки?

Я нашел предложение в Интернете, что, синхронизируя метод запуска, вы могли бы потенциально создать очередь де-факто streamов, например:

  public void createThreadQueue(){ ThreadedClass a = new ThreadedClass(); new Thread(a, "First one").start(); new Thread(a, "Second one, waiting on the first one").start(); new Thread(a, "Third one, waiting on the other two...").start(); } 

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

Синхронизация метода run() Runnable совершенно бессмысленна, если вы не хотите разделить Runnable среди нескольких streamов, и вы хотите упорядочить выполнение этих streamов. Это в основном противоречие в терминах.

Существует теоретически другой гораздо более сложный сценарий, в котором вы можете синхронизировать метод run() , который снова включает в себя совместное использование Runnable среди нескольких streamов, но также использует wait() и notify() . Я никогда не сталкивался с этим в течение 14 лет Java.

Существует 1 преимущество использования synchronized void blah() над void blah() { synchronized(this) { и это ваш байт-код будет в 1 байт короче, так как синхронизация будет частью сигнатуры метода вместо самой операции , Это может повлиять на возможность встроить метод компилятором JIT. Помимо этого нет никакой разницы.

Лучшим вариантом является использование внутренней private final Object lock = new Object() чтобы кто-то не мог заблокировать ваш монитор. Он достигает такого же результата без недостатка злой внешней блокировки. У вас есть этот дополнительный байт, но он редко меняет ситуацию.

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

 public class ThreadedClass implements Runnable{ private final Object lock = new Object(); public void run(){ synchronized(lock) { while(true) //do some stuff in a thread } } } } в public class ThreadedClass implements Runnable{ private final Object lock = new Object(); public void run(){ synchronized(lock) { while(true) //do some stuff in a thread } } } } 

Редактировать в ответ на комментарий:

Рассмотрим, что такое синхронизация: он предотвращает ввод других streamов в один и тот же блок кода. Итак, представьте себе, что у вас есть class, подобный приведенному ниже. Предположим, что текущий размер равен 10. Кто-то пытается выполнить добавление, и это приводит к изменению размера массива поддержки. Хотя они находятся в середине изменения размера массива, кто-то вызывает makeExactSize(5) в другом streamе. Теперь вы внезапно пытаетесь получить доступ к data[6] и он бомбит вас. Предполагается, что синхронизация предотвратит это. В многопоточных программах вам нужна просто NEED-синхронизация.

 class Stack { int[] data = new int[10]; int pos = 0; void add(int inc) { if(pos == data.length) { int[] tmp = new int[pos*2]; for(int i = 0; i < pos; i++) tmp[i] = data[i]; data = tmp; } data[pos++] = inc; } int remove() { return data[pos--]; } void makeExactSize(int size) { int[] tmp = new int[size]; for(int i = 0; i < size; i++) tmp[i] = data[i]; data = tmp; } } 

Зачем? Минимальная дополнительная безопасность, и я не вижу никаких правдоподобных сценариев, где это будет иметь значение.

Почему нет? Это не стандарт. Если вы кодируете как часть команды, когда какой-либо другой участник видит ваш синхронизированный run он, вероятно, потратит 30 минут, пытаясь понять, что является таким особенным либо с вашим run либо с каркасом, который вы используете для запуска Runnable ,

По моему опыту, нецелесообразно добавлять «синхронизированное» ключевое слово к методу run (). Если нам нужно синхронизировать несколько streamов или нам нужна streamобезопасная очередь, мы можем использовать более подходящие компоненты, такие как ConcurrentLinkedQueue.

Хорошо, что теоретически можно вызвать метод запуска без проблем (ведь это общедоступно). Но это не значит, что нужно это делать. Таким образом, в принципе нет оснований для этого, кроме добавления незначительных накладных расходов на вызов вызова thread (). Хорошо, если вы используете экземпляр несколько раз, вызывая new Thread хотя я и не уверен, что это законно с помощью API streamовой передачи и b) кажется совершенно бесполезным.

Также ваш createThreadQueue не работает. synchronized по нестационарному методу, синхронизирует объект экземпляра (т. е. this ), поэтому все три streamа будут выполняться параллельно.

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

 class Kat{ public static void main(String... args){ Thread t1; // MyUsualRunnable is usual stuff, only this will allow concurrency MyUsualRunnable m0 = new MyUsualRunnable(); for(int i = 0; i < 5; i++){ t1 = new Thread(m0);//*imp* here all threads created are passed the same runnable instance t1.start(); } // run() method is synchronized , concurrency killed // uncomment below block and run to see the difference MySynchRunnable1 m1 = new MySynchRunnable1(); for(int i = 0; i < 5; i++){ t1 = new Thread(m1);//*imp* here all threads created are passed the same runnable instance, m1 // if new insances of runnable above were created for each loop then synchronizing will have no effect t1.start(); } // run() method has synchronized block which lock on runnable instance , concurrency killed // uncomment below block and run to see the difference /* MySynchRunnable2 m2 = new MySynchRunnable2(); for(int i = 0; i < 5; i++){ // if new insances of runnable above were created for each loop then synchronizing will have no effect t1 = new Thread(m2);//*imp* here all threads created are passed the same runnable instance, m2 t1.start(); }*/ } } class MyUsualRunnable implements Runnable{ @Override public void run(){ try {Thread.sleep(1000);} catch (InterruptedException e) {} } } class MySynchRunnable1 implements Runnable{ // this is implicit synchronization //on the runnable instance as the run() // method is synchronized @Override public synchronized void run(){ try {Thread.sleep(1000);} catch (InterruptedException e) {} } } class MySynchRunnable2 implements Runnable{ // this is explicit synchronization //on the runnable instance //inside the synchronized block // MySynchRunnable2 is totally equivalent to MySynchRunnable1 // usually we never synchronize on this or synchronize the run() method @Override public void run(){ synchronized(this){ try {Thread.sleep(1000);} catch (InterruptedException e) {} } } } 

на самом деле очень просто оправдать «синхронизацию или не синхронизацию»,

если ваш вызов метода может изменить внутреннее состояние вашего объекта, тогда «синхронизация» в противном случае не требуется

простой пример

 public class Counter { private int count = 0; public void incr() { count++; } public int getCount() { return count; } } 

в приведенном выше примере incr () необходимо синхронизировать, так как он изменит значение val, в то время как синхронизация getCount () не нужна

однако есть еще один угловой случай, если count – java.lang.Long, Double, Object, то вам нужно объявить как

 private volatile long count = 0; 

чтобы обновление ref было атомарным

в основном это то, что вам нужно думать о 99% времени при работе с многопоточным

  • Как сделать мой ArrayList streamобезопасным? Другой подход к проблеме в Java?
  • Как синхронизировать базу данных SQLite на телефоне Android с базой данных MySQL на сервере?
  • Заказ streamов для запуска в том порядке, в котором они были созданы / запущены
  • C # версия синхронизированного ключевого слова java?
  • Синхронизация IPC с общей памятью (без блокировки)
  • Зачем использовать ReentrantLock, если вы можете использовать синхронизированный (это)?
  • Как сделать блокировку с несколькими чтениями / одиночной записью из более простых примитивов синхронизации?
  • Коллекции. Синхронизированный список и синхронизация
  • Почему не логично синхронизировать логику?
  • Как использовать свойство CancellationToken?
  • синхронизированный блок для объекта Integer
  • Давайте будем гением компьютера.