#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
вы будете печатать что-то.