#java #synchronized
Не могу понять, почему данный код не работает корректно.
import java.util.concurrent.atomic.AtomicInteger;
public class Test1 extends Thread {
public static AtomicInteger counter = new AtomicInteger(0);
synchronized void doWork1() {
counter.incrementAndGet();
counter.incrementAndGet();
System.out.println("Thread+++" + " - " + Test1.counter);
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
}
}
synchronized void doWork2() {
counter.decrementAndGet();
counter.decrementAndGet();
System.out.println("Thread---" + " - " + Test1.counter);
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
}
}
public void run() {
for (int i = 0; i < 100; i++) {
doWork1();
doWork2();
}
}
public static void main(String[] args) {
Test1 test1 = new Test1();
test1.start();
Test1 test2 = new Test1();
test2.start();
}
}
Ответы
Ответ 1
При указании ключевого слова synchronized для методов в качестве монитора, который захватывается потоками, используется соответствующий экземпляр класса. Так как у вас два разных экземпляра класса Test1 - потоки в принципе работают независимо друг от друга. Для того, чтобы решить эту проблему, необходимо использовать общий монитор для обоих потоков, например, отдельный объект: private final static Object lock = new Object(); void doWork1() { synchronized(lock) { ... } } void doWork2() { synchronized(lock) { ... } } Таким образом, потоки уже будут ждать освобождения монитора перед выполнением кода внутри блока synchronized.Ответ 2
Как уже было отмечено выше, синхронизация в случае нестатических методов происходит по объекту, вызывающему этот метод. В данном случае есть два разных экземпляра класса Test1, локи захватываются на разные объекты. Решить проблему можно несколькими путями: либо захватить лок на весь класс: void doWork1() { synchronized(Test1.class) { ... } } либо захватить лок на один и тот же объект( в данном случае можно на counter) void doWork1() { synchronized(counter) { ... } } Я бы предпочел второй вариант.Ответ 3
incrementAndGet - да, атомарна, но два таких оператора написанных друг за другом не являются таковыми Вот смотри, например, один поток вошел в doWork1 и выполнил операцию counter.incrementAndGet() и в этот момент управление перешло к другому потоку который начинает выполнять doWork2 и выполняет его до конца. на его выходе получишь нечетное число.
Комментариев нет:
Отправить комментарий