Обе темы застряли в состоянии ожидания в Java - PullRequest
1 голос
/ 27 января 2020

У меня есть сервер и два клиента, сервер запускает два потока (ServerHandler), которым обоим передается Socket TCP-соединение соответствующих клиентов, которые вначале подключаются к серверу.

Ожидаемое поведение:
Один поток ServerHandler отправляет сообщение клиенту, в то время как другой поток ServerHandler ожидает wait() ... затем рабочий поток уведомляет спящий поток и ожидает ... и т. Д.

Фактическое поведение:
Оба ServerHandlers ждут одновременно. Они оба входят в синхронизированный блок, чего не должно быть, один поток должен ждать, пока другой работает.

Фрагмент кода ServerHandler (запущены два его экземпляра)

private static Object lock = new Object();
...
@Override
public void run() {
    System.out.println(String.format("  --> Server handler: %s is in run method...", serverID));
    while (true) {
        synchronized (lock){
            while (!Server.isFinished()) {
                try {
                    System.out.println(String.format("  --> Server handler: %s is waiting...", serverID));
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println(String.format("  --> Server handler: %s is ready to send board...", serverID));
            Server.setFinished(true);
            sendBoard();

            notify();
        }
    }
}

Примечание: Класс Server запускает оба потока ServerHandler. finished установлено по умолчанию false

Выход:

CLIENT connected!
<-- I'M Alan I want to play!
--> Server handler 1 instantiated!
CLIENT connected!
<-- I'M Bot I want to play!
--> Server handler 2 instantiated!
HANDLER started...
HANDLER started...
--> Server handler: 1 is in run method...
--> Server handler: 2 is in run method...
--> Server handler: 1 is waiting...
--> Server handler: 2 is waiting...

Ожидаемое поведение:

CLIENT connected!
<-- I'M Bot I want to play!
--> Server handler 1 instantiated!
CLIENT connected!
<-- I'M Alan I want to play!
--> Server handler 2 instantiated!
HANDLER started...
HANDLER started...
--> Server handler: 1 is in run method...
--> Server handler: 1 is waiting...
--> Server handler: 2 is in run method...
--> Server handler: 2 is ready to send board...
...

Спасибо!

Ответы [ 3 ]

0 голосов
/ 28 января 2020

Не уверен, что вы используете правильный механизм синхронизации здесь.

Ваш поток выглядит следующим образом

  1. Поток 1 захватывает монитор
  2. Поток 1 проверяет состояние сервера
  3. Сервер не готов
  4. Поток 1 отбрасывает монитор и ожидает
  5. Поток 2 захватывает монитор
  6. Поток 2 проверяет состояние сервера
  7. Сервер не готов
  8. Поток 2 отбрасывает монитор и ждет
  9. Тупик, поскольку оба потока ожидают друг друга

Поэтому потоки не могут пройти проверку сервера, поскольку никто уведомит, что они могут продолжить. Чтобы это было возможно, вам нужен либо поток 1, либо поток 2, чтобы уведомить другой, когда оба будут готовы, что приведет к тупику.

Возможно, вы захотите рассмотреть, например, использование Cycli c Барьер вместо. Это позволит вам подождать, пока вы не наберете достаточно потоков, чтобы начать игру.

private static Object lock = new Object();
private static CyclicBarrier barrier = new CyclicBarrier(2, () -> setReady());
...
@Override
public void run() {
    System.out.println(String.format("  --> Server handler: %s is in run method...", serverID));
    while (true) {
        synchronized (lock){
            while (!Server.isFinished()) {
                try {
                    System.out.println(String.format("  --> Server handler: %s is waiting...", serverID));
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
           }
           System.out.println(String.format("  --> Server handler: %s is ready to send board...", serverID));

            sendBoard();

            notify();
        }
    }
}

public static void setReady() {
    synchronized (lock) {
        try {
            sendBoard();
            System.out.println(String.format("  --> Server handler: %s is waiting...", serverID));
            lock.notify();
            lock.wait();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Нечто подобное позволит изменить блок до тех пор, пока оба потока не запустятся, второй поток не достигнет барьера, а затем не вызовет метод setReady, отправляющий доску перед ожиданием в другом потоке.

0 голосов
/ 02 февраля 2020

Вы должны вызвать notify для lock объекта.

0 голосов
/ 27 января 2020

Можете ли вы привести некоторые примеры ожидаемого результата?

Одна вещь, которая кажется случайной ошибкой, состоит в том, что оба потока имеют собственную блокировку вместо одной общей (обратите внимание на stati c).

Другая вещь, которая не имеет смысла, это вызов notify для блокировки, которой вы уже владеете.

private static Object lock = new Object();

public void run() {
    while (true) {
        synchronized (lock) {
            while (!Server.isFinished()) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }

            Server.setFinished(true);
            sendBoard();
        }
    }
}
...