Рассмотрим эту неудачную последовательность событий. Thread1 увеличивает значение, устанавливает флаг в true и уведомляет все потоки в ожидании, установленном для блокировки. Теперь Thread0 уже был в наборе ожидания. Затем Thread0 просыпается, и его флаг = false. Затем Thread0 выходит из цикла while, печатает увеличенное значение и уведомляет все ожидающие потоки. Затем он переходит к следующей итерации в цикле while и вызывает wait для объекта блокировки. Но Thread1 не находится в состоянии ожидания, скорее он выключается из CPU планировщиком после завершения его синхронизированного блока, чтобы дать возможность Thread0. Поток 1 находится в состоянии выполнения и ему снова предоставляется шанс по расписанию, так как не осталось никаких выполняемых потоков. Затем Tread1 входит в цикл while, поскольку flag = true, и вызывает wait для того же объекта блокировки. Теперь оба потока находятся в состоянии ожидания, и их некому разбудить. Так что это хороший пример живой блокировки в системе.
Это происходит из-за того, что флаг является полем экземпляра и поэтому не используется совместно для потоков. Таким образом, каждый поток имеет свою собственную копию флага. Если вы пометите его как статическую переменную, тогда оба потока будут использовать это значение, поэтому проблема решена. Вот как должна выглядеть декларация флага.
static boolean flag = false;
Как это решает проблему? Ну, рассмотрим ту же последовательность событий. И теперь Thread1 устанавливает значение флага в true, прежде чем он вызовет notify для объекта блокировки. Thread0 уже находится в состоянии ожидания. Schedular отключает Thread1 от процессора и дает шанс Thread0. Он запускается и, поскольку flag = true, он входит в цикл while, устанавливает флаг в false и вызывает ожидание для объекта блокировки. Затем Thread0 переходит в состояние ожидания и Schedular дает шанс Thread1. Thread1 возобновляет выполнение и flag = false, следовательно, он выходит из цикла while, печатая увеличенное значение и уведомляя ожидающий поток. Так что сейчас нет живой блокировки.
Однако я не вижу смысла в использовании synchronized
и неблокирующих атомарных переменных. Вы не должны использовать их обоих вместе. Более качественная и эффективная реализация приведена ниже.
public class Sequence extends Thread {
private static final Object lock = new Object();
private static int integer = 0;
static boolean flag = false;
@Override
public void run() {
while (true) {
synchronized (lock) {
while (flag) {
flag = false;
try {
System.out.println(Thread.currentThread().getName() + " waiting");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " " + ++integer);
flag = true;
System.out.println(Thread.currentThread().getName() + " notifying");
lock.notify();
}
}
}
public static void main(String[] args) {
Sequence sequence1=new Sequence();
Sequence sequence2=new Sequence();
sequence1.start();
sequence2.start();
}
}