Проблема, связанная с попыткой синхронизировать два потока

#java #multithreading #synchronized

#java — язык #многопоточность #синхронизировано

Вопрос:

Я пытался выполнить синхронизацию с двумя потоками, один из которых печатал «ping», а другой — «pong», чтобы напечатать что-то вроде «пинг-понг, пинг-понг, пинг-понг …» Я написал следующую программу для достижения этого, но не смог получить ожидаемый результат. Интересно, какую ошибку я совершаю. Решения для того же доступны в Интернете, но я подумал, что должен попробовать сам, прежде чем искать легкодоступный ответ.

 class MyThread implements Runnable{
  String val = null;
  MyThread(String val) {
      this.val = val;
  }

    public void run() {
        while(true){
            PingPong.pintMessage(val);
        }
    }
}

public class PingPong {
   static Integer turn = 1;

   public static void main(String args []) {
       Thread t1 = new Thread(new MyThread("ping"));
       Thread t2 = new Thread(new MyThread("pong"));
       t1.start();
       t2.start();

   }
   public static void pintMessage(String msg) {
       synchronized (turn) {
         if(turn==1) {
             System.out.println(Thread.currentThread().getName() " " msg);
             turn=2;
         }
         else {
             System.out.println(Thread.currentThread().getName() " " msg);
             turn = 1;
         }
    }
   }
}
  

Вывод :

 Thread-0 ping
Thread-1 pong
Thread-1 pong
Thread-1 pong
Thread-0 ping
Thread-0 ping
Thread-0 ping
Thread-1 pong
Thread-1 pong
Thread-1 pong
Thread-0 ping
Thread-0 ping
Thread-0 ping
Thread-1 pong
Thread-1 pong
Thread-1 pong
Thread-0 ping
Thread-0 ping
Thread-0 ping
Thread-1 pong
Thread-1 pong
Thread-1 pong
Thread-0 ping
Thread-0 ping
Thread-0 ping
Thread-1 pong
Thread-1 pong
Thread-1 pong
Thread-0 ping
Thread-0 ping
Thread-0 ping
Thread-1 pong
  

Пожалуйста, сообщите, какую фундаментальную ошибку я здесь совершаю, и если есть лучший подход для достижения такого же поведения. Спасибо!!

После внесения предложенных изменений: я попытался использовать связь с потоком, используя wait / notify, но получаю исключение IllegalMonitorStateException. Я предполагаю, что я использовал wait / notify для объекта, на котором я получаю блокировку (wait / notify используется в синхронизированном контексте). Пожалуйста, помогите мне понять тонкость концепции. Спасибо!

 class MyThread implements Runnable{
    String val = null;

    MyThread(String val) {
        this.val = val;
    }

    public void run() {
        while(true){
            try {
                PingPong.printMessage(val);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

public class PingPong {
    static Integer turn = 1;

    public static void main(String args []) {
        Thread t1 = new Thread(new MyThread("ping"));
        Thread t2 = new Thread(new MyThread("pong"));
        t1.start();
        t2.start();
    }
    public static void printMessage(String msg) throws InterruptedException {
        synchronized (turn) {
            if(turn==1) {
                System.out.println(Thread.currentThread().getName() " " msg);
                turn=2;
            }
            else {
                System.out.println(Thread.currentThread().getName() " " msg);
                turn = 1;
            }
            turn.notifyAll();
            turn.wait();
        }
    }
}

Output :
Thread-0 ping
Exception in thread "Thread-0" Thread-1 pong
java.lang.IllegalMonitorStateException
    at java.lang.Object.notifyAll(Native Method)
    at PingPong.printMessage(PingPong.java:40)
    at MyThread.run(PingPong.java:12)
    at java.lang.Thread.run(Unknown Source)
  

Комментарии:

1. какой результат вы получаете?

2. Привет, страшно, я только что отредактировал вопрос, чтобы отразить полученный результат. Спасибо!

3. printMessage не гарантирует, что ваш поток ping всегда будет иметь номер очереди 1, а pong — номер очереди 2. Что касается метода, номер хода не связан с текущим выполняемым потоком. Чтобы получить желаемый результат, вам нужно найти способ обмена данными между потоками, чтобы ping знал свою очередь

4. @conscells: Было бы полезно, если бы вы могли предложить способ, позволяющий потоку ping знать, является ли он только turn.

5. Я не упомянул, потому что вы сказали, что вам не нужен легкодоступный ответ. Но вы могли бы изучить сигнализацию потоков или очереди блокировки, чтобы заставить несколько потоков взаимодействовать. Я бы также рекомендовал пройти через tutorials.jenkov.com/java-concurrency/index.html .

Ответ №1:

вы неправильно поняли концепцию synchonized .

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

В вашем случае: у каждого потока есть возможность выполнить цикл несколько раз, прежде чем JVM установит его в фоновый режим для выполнения другого потока.

Если вы хотите обеспечить определенный порядок, вы должны использовать wait() и notify() / notifyAll() : ([редактировать]: этот код может работать некорректно (не тестировался), это просто для демонстрации принципа …)

 class MyThread implements Runnable{
  private Object other;
  public void setOther (MyThread other){
    this.other = other;

  }
  //no changes
       while(true){
            PingPong.pintMessage(val);
            notifyAll();
            other.wait();
        }

}

public static void pintMessage(String msg) {
    synchronized (turn) {
     // no changes
      // MyThread.semaphore.notifyAll();
 }
}    
  

смотрите здесь для получения дополнительной информации https://docs.oracle.com/javase/tutorial/essential/concurrency /

Ответ №2:

В вашем, pintMessage независимо от того, какое значение turn вы будете печатать что-то.