Я изучал механизмы синхронизации Java старой школы и столкнулся с некоторыми проблемами.Код, доступный на GitHub .
Рассмотрим следующий пример теста.
Из основного метода мы создаем объект гаража Platform
, представленный матрицей символов.Есть две машины, уже припаркованные (обозначенные x ), и стоящее на пути транспортное средство (обозначенное v ).Автомобили въезжают в гараж из положения (1,0) и проезжают, как показано красным, смотря вправо и влево на доступное место для парковки.
Из основного метода создается тридцать транспортных средств (потоков) и вызывается метод start()
.Если перед ними находится машина, преграждающая им дорогу, они должны вызвать wait()
на общем объекте Platform.lock
.
Класс Observer
отвечает за вывод текущего состояния гаража на консоль.
При возникновении проблем возникает нить MysteryVehicle
, единственной задачей которой является удалениепрепятствие (обозначается v ) и позволяет машинам проехать упорядоченным образом.Кажется, что этот объект никогда не получает монитор замка.
Основной класс
package garage;
import garage.model.*;
import javafx.application.Application;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Platform platform = new Platform();
Vehicle.platform = platform;
platform.print();
Vehicle[] vehicles = new Vehicle[30];
for (int i = 0; i < 30; i++) {
vehicles[i] = new Vehicle();
}
for (int i = 0; i < 30; i++) {
vehicles[i].start();
}
Observer observer = new Observer();
observer.platform = platform;
observer.setDaemon(true);
observer.start();
MysteryVehicle mysteryVehicle = new MysteryVehicle();
mysteryVehicle.start();
try {
mysteryVehicle.join();
} catch (Exception exception) {
exception.printStackTrace();
}
try {
for (int i = 0; i < 30; i++)
vehicles[i].join();
} catch (Exception exception) {
exception.printStackTrace();
}
platform.print();
System.out.println("END");
System.out.println(platform.flag); // checks whether wait() was called anytime
}
public static void main(String[] args) {
launch(args);
}
}
Класс платформы
package garage.model;
public class Platform {
public boolean flag = false; // indicades whether wait() was called anytime
public char[][] fields = new char[10][8];
public static final Object lock = new Object();
public Platform() {
for (int i = 0; i < 10; i++)
for (int j = 0; j < 8; j++)
fields[i][j] = '.';
fields[1][0] = fields[2][0] = 'x'; // already parked cars
fields[4][1] = 'v'; // obstacle
}
public void print() {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(fields[i][j]);
}
System.out.println();
}
System.out.println();
}
}
}
Класс автомобиля
package garage.model;
public class Vehicle extends Thread {
int x = 1;
int y = 0;
private int block = 1;
public static Platform platform;
public boolean moving = true;
public void run() {
y++; // entrance
while (moving) {
if (block == 1) {
while (moving && platform.fields[x + 1][y] == 'v')
try {
synchronized (Platform.lock) {
Platform.lock.wait();
platform.flag = true;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Platform.lock) {
x++;
Platform.lock.notifyAll();
yield(); // suggest another vehicle should get processor time
}
// looking left and right for an available spot
if (platform.fields[x][0] != 'x' && platform.fields[x][0] != '*') {
platform.fields[x][0] = '*'; // park
moving = false;
} else if (x < 8 && platform.fields[x][3] != 'x' && platform.fields[x][3] != '*'
&& platform.fields[x][2] != 'v') {
platform.fields[x][3] = '*';// park
moving = false;
}
// checking if we reached the end
if (moving && x == 9) {
block = 2;
move(); // transfer to the second block of the garage
}
} // end block 1
if (block == 2) {
// looking left and right for an available spot
if (platform.fields[x][7] != 'x' && platform.fields[x][7] != '*') {
platform.fields[x][7] = '*'; // park
moving = false;
} else if (x < 8 && platform.fields[x][4] != 'x' && platform.fields[x][4] != '*'
&& platform.fields[x][5] != 'v') {
platform.fields[x][4] = '*'; // park
moving = false;
}
while (moving && platform.fields[x - 1][y] == 'v')
try {
synchronized (Platform.lock) {
Platform.lock.wait(); // waiting for the mystery vehicle to disappear
platform.flag = true;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
if (moving) {
synchronized (Platform.lock) {
x--;
Platform.lock.notifyAll();
yield();
}
}
if (x == 1) {
y++;
moving = false;
}
} // end block 2
} // end moving
}
private void move() {
while (y != 6) {
if (y + 1 == 'v')
try {
synchronized (Platform.lock) {
platform.flag = true;
Platform.lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Platform.lock) {
y++;
Platform.lock.notifyAll();
yield();
}
}
}
}
Класс наблюдателя
package garage.model;
public class Observer extends Thread {
public Platform platform;
{
setPriority(MIN_PRIORITY);
}
@Override
public void run() {
while (true) {
synchronized (Platform.lock) {
try {
sleep(2000);
platform.print();
yield();
} catch (InterruptedException exception) {
exception.printStackTrace();
} finally {
Platform.lock.notifyAll();
}
}
}
}
}
Класс MysteryVehicle
package garage.model;
public class MysteryVehicle extends Vehicle {
{
setPriority(MAX_PRIORITY);
}
@Override
public void run() {
synchronized (Platform.lock) {
System.out.println("And the vehicle disappears!");
platform.fields[4][1] = '.';
}
}
}
Через некоторое время Observer
выдает следующий вывод, который никогда не меняется.Похоже, что MysteryVehicle никогда не получал процессорное время, и все машины зависли.
Что мне здесь не хватает?
ОБНОВЛЕНИЕ:
Поскольку yield()
не снимает блокировку, я попытался использовать wait()
вместо этого - и получил IllegalMonitorStateException
, хотя wait()
внутри блока синхронизированта же самая блокировка!
Я пытался изменить Observer
на TimerTask
, запланированный на Timer
на каждые 5 секунд.Вывод был намного быстрее, и программа не завершилась.