В общем, задача уже решена (решил не отправлять на проверку, чтобы была возможность прикрепить сюда код) благодаря синхронизации через класс, однако мне неясен один момент. Я решил сделать тест, ведь я не понял, почему нельзя было сделать синхронизацию через this в методе run. По логике, нити ждут входа в блок, если он занят, потом одна нить входит, инкрементирует count, инкрементирует значение массива и выходит. По идее, так должна была создаваться очередь, каждая нить входит, делает действия ранее описанные, выходит, и так по кругу, как очередь (хоть и хаотичная, но очередь). А теперь самое интересное:
.........
Thread-1: entered synchronized block.
Thread-1: escaped synchronized block.
Thread-3: entered synchronized block.
Thread-1: entered synchronized block.
Thread-3: escaped synchronized block.
Thread-1: escaped synchronized block.
Thread-2: entered synchronized block.
Thread-2: escaped synchronized block.
Thread-3: entered synchronized block.
Thread-3: escaped synchronized block.
Thread-3: entered synchronized block.
Thread-3: escaped synchronized block.
.........
Каким образом нить 1 прокралась в синхронизированный блок, когда он был заблокирован нитью 3? Почему синхронизированный блок дает сбой?
package com.javarush.task.task17.task1722;
/*
Посчитаем
*/
public class Solution {
public static void main(String[] args) throws InterruptedException {
Counter counter1 = new Counter();
Counter counter2 = new Counter();
Counter counter3 = new Counter();
Counter counter4 = new Counter();
counter1.start();
counter2.start();
counter3.start();
counter4.start();
counter1.join();
counter2.join();
counter3.join();
counter4.join();
for (int i = 1; i <= 100; i++) {
if (values[i] != 1) {
System.out.println("Массив values содержит элементы неравные 1");
System.out.println(String.format("values[%d] = %d", i, values[i]));
break;
}
}
}
public static Integer count = 0;
public static int[] values = new int[105];
static {
for (int i = 0; i < 105; i++) {
values[i] = 0;
}
}
public synchronized static void incrementCount() {
count++;
}
public synchronized static int getCount() {
return count;
}
public static class Counter extends Thread {
@Override
public void run() {
do {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + ": entered synchronized block.");
incrementCount();
values[getCount()]++;
System.out.println(Thread.currentThread().getName() + ": escaped synchronized block.");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
} while (getCount() < 100);
}
}
}
ответвопрос :) синхронизироваться стоит по тому объекту, который изменяете. если же в блоке synchronized вы изменяете сразу несколько объектов - тогда синхронизируйтесь по локу (по сути, по какому-то любому объекту, общему для всех нитей, выполняющих этот код). вы в своем решении синхронизировались по this.getClass() как я понимаю, то-есть по объекту класса этого треда, соответственно для всех нитей, которые выполнялись - класс был один и тот же, поэтому и решение прошло. если же у вас есть какой-то метод, который могут выполнять разные нити разных классов, которые наследуются от Thread - тогда вариант с синхронизацией по классу нити может не сработать, потому что классы этих нитей могут быть разные. в общем, выбирайте тот вариант синхронизации, который лучше всего подходит для вашей задачи :)