#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 и выполняет его до конца. на его выходе получишь нечетное число.
Комментариев нет:
Отправить комментарий