#java #multithreading #concurrency #synchronization
Вопрос:
public class SyncBlockIssue { public static void main(String[] args) { Thread r1= new Multi(); Thread t1= new Thread(r1); Thread t2= new Thread(r1); t1.start(); t2.start(); } } class Multi extends Thread { Integer count =new Integer(0); @Override public void run() { for(int i=0;ilt;100000;i ) { synchronized (count) { //synchronized (this) { //working fine with this count ; } } System.out.println(Thread.currentThread().getName() " " count); } }
Приведенный выше код печатает 2 миллиона из 2-го потока, независимо от того, сколько раз я запускаю, когда получаю блокировку на «этом», но где при печати произвольно учитывается, когда приобретается объект блокировки «подсчета». не мог бы кто-нибудь объяснить разницу.
Комментарии:
1. Примечание в общем стиле кода: никогда не передавайте поток конструктору другого потока. Ваш класс
Multi
не должен расширятьсяThread
, а просто реализовыватьсяRunnable
. Меньший момент: никогда не используйтеnew
при упаковке примитивный тип. ПростоInteger count = 0;
сойдет. Начиная с Java 9, вы получите предупреждение при использованииnew Integer(0)
Ответ №1:
Вот мое предположение, так как я не могу использовать комментарии для ввода такого количества слов.
Integer
объекты неизменяемы:
/** * The value of the {@code Integer}. * * @serial */ private final int value; /** * Constructs a newly allocated {@code Integer} object that * represents the specified {@code int} value. * * @param value the value to be represented by the * {@code Integer} object. */ public Integer(int value) { this.value = value; }
Итак, когда вы это сделаете count
, Integer
будет создан новый объект. Затем count
ссылка направляется на новый объект.
Предположим, что следующий поток:
Thread r1
приобретаетcount
блокировку (обратите внимание, чтоcount object
блокируется), в этот моментthread r2
блокируется.r1
вызываетcount
, создается новыйInteger
объект. Затемcount
ссылка направляется на новый объект. Иr1
отпустите замок.(Эта блокировка относится к предыдущему целочисленному объекту).r2
который был заблокирован и заблокирован.r1
также получает блокировку, потомуcount
что уже указал на новый объект.- Оба потока выполняются
count
: возникает проблема.
Следующий код доказывает, что оба потока удерживают «блокировку» одновременно
for(int i=0; ilt;2; i ) { synchronized (count) { System.out.println(Thread.currentThread().getName() "get lock"); count ; if (i == 1) { try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() "release lock"); } }
Комментарии:
1. Обратите внимание , что когда у вас есть такая конструкция, как
synchronized (count) …
блок чтенияcount happens outside the
synchronized, прежде чем его можно будет ввести. Таким образом, нет никакой гарантии, что поток увидит новые значения, написанные другим потоком внутриsynchronized
блока, поскольку эта гарантия существует только для правильной синхронизации кода с одним и тем же объектом. Таким образом , даже если второй поток еще не достиг этой точки, когда это делает первыйcount
, он все равно может работать со старым объектом, в то время как первый продолжает работу с новым.2. Существует также гонка данных, которая может привести к странному поведению.