java 如何使用好同步块?

rvpgvaaj  于 2022-12-02  发布在  Java
关注(0)|答案(2)|浏览(104)

我想打印“ping”“pong”,它们在同一个类中,但方法不同,使用同步块打印5次。
问题是它在打印乒乓一次后就停止了。
如何打印乒乓球5次?
我想我把notifyAll()和wait()放在了正确的位置。
打印结果

ping
pong

这是我的主类

public class ThreadTest2 {

    public static void main(String[] args) throws {
        Thread thread1 = new Thread(() -> {
            forLoop("a");
        });
        Thread thread2 = new Thread(() -> {
            forLoop(null);
        });

        thread1.setPriority(10);
        thread2.setPriority(1);
        thread1.start();
        thread2.start();
    }

    static void forLoop(String target) {
        AA aa = new AA();
        try {
            for(int i=0; i<5; i++){
                if(target != null){
                    aa.ping();
                }
                else{
                    aa.pong();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这是我的乒乓球课

public class AA {

    Thread thread;
    public void ping() throws InterruptedException {
        synchronized (this) {
            System.out.println("ping");
            wait();   
            notifyAll();     
        }
    }

    public void pong() throws InterruptedException {
        synchronized (this) {
                System.out.println("pong");
                notifyAll();
                wait();
        }
    }
}

谢谢你,谢谢你

ping
pong
ping
pong
ping
pong
ping
pong
ping
pong
sz81bmfz

sz81bmfz1#

问题是它在打印乒乓一次后就停止了。
我真的不喜欢这些学术性的问题。让两个线程彼此锁步正是你不想做的,因为线程被设计为异步运行,协调频率更低。
您的代码有许多问题:

  • 每次调用forloop()方法都会创建一个新的AA示例。因为您在每个线程中都调用它,所以每个线程都将锁定并等待AA的 * 不同 * 示例,这样它们就看不到另一个线程的notifyAll(),并且总是死锁。您需要创建一个AA,并将同一个示例传递给两个for循环。
  • 在这两种情况下,您都需要在 * wait()之前 * 调用notifyAll(),否则ping线程可能会在 * 唤醒pong线程之前 * 等待,从而导致死锁。
  • 不能保证初始ping会在pong之前运行,这很难正确解决,一个(丑陋的)解决方案是在AA中的boolean firstPrinted字段,并在println(...)之后将其设置为true,然后让pong执行如下操作:
synchronized (this) {
      while (!first) {
          wait();
      }
  }
  • 一旦其中一个线程在5次迭代后完成,另一个线程仍将等待,因此将永远不会退出。您需要设法跳过最后一次等待。这里的一个选项是在for 1 to 5循环完成后添加对aa.oneMoreNotify()的调用:
public void oneMoreNotify() {
      synchronized (this) {
          notifyAll();
      }
  }

其他意见:

  • thread.setPriority(...)实际上做得很少,除非你有非常CPU绑定的计算循环。这些应该被删除。
  • 当你使用catch (InterruptedException e)时,你应该立即调用Thread.currentThread().interrupt()作为一个好的模式。
  • 传入String target而不是boolean是一种奇怪的模式。
  • 你可以考虑把要打印的消息作为参数传递给forloop,这样ping和pong就可以调用同一个方法,第一个等待逻辑应该发生在message.equals("pong")
  • AA中的Thread字段未使用,会造成混淆。
f1tvaqid

f1tvaqid2#

  • 您可以这样修改代码:每个线程控制自己循环
  • 必须先执行notifyAll(),然后再执行wait()
  • 如果有3个线程,则需要通过全局变量进行控制
public class SynchronizedTest {
    public void ping(int count) throws InterruptedException {
        synchronized (this) {
            for (int i = 0; i <count ; i++) {
                System.out.println("ping");
                notifyAll();//
                wait();
            }
        }
    }

    public void pong(int count) throws InterruptedException {
        synchronized (this) {
            for (int i = 0; i <count ; i++) {
                System.out.println("pong");
                notifyAll();
                wait();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedTest aa = new SynchronizedTest();
        Thread thread1 = new Thread(() -> {
            try {
                aa.ping(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                aa.pong(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.start();
        thread2.start();
    }
}

相关问题