【问题标题】:PingPong Program Java MultithreadingPingPong 程序 Java 多线程
【发布时间】:2017-12-27 06:43:42
【问题描述】:

我正在尝试学习多线程的基本概念。

为什么我的乒乓程序只打印 Ping0 和 Pong0,为什么 notify() 没有启动处于等待状态的 Ping 线程?

public class PingPong 实现 Runnable { 串词;

  public PingPong(String word) {
    this.word = word;
  }

  public void run() {

    synchronized (this) {
      for (int i = 0; i < 10; i++) {
        System.out.println(word + i);
        try {
          wait(); 
          notifyAll();
        } catch (Exception e) {
          System.out.println(e.getMessage());
        }
      }
    }
  }

  public static void main(String[] args) {

    Runnable p1 = new PingPong("ping");
    Thread t1 = new Thread(p1);
    t1.start();

    Runnable p2 = new PingPong("pong");
    Thread t2 = new Thread(p2);
    t2.start();

  }

}

输出

ping0
pong0

我尝试删除 wait() 并且它正在打印 ping pong 直到循环结束。但这能保证它会按顺序打印吗?

为什么wait()后跟notify()不要求ping1线程开始执行?

【问题讨论】:

    标签: multithreading


    【解决方案1】:
    1. 如果您看到 jstack,您可以看到 thread-0 和 thread-1 正在等待不同的锁。那是因为你的 p1 和 p2 是不同的对象,所以当你使用synchronized (this) 时,它们不会竞争同一个锁,所以这种方式通知是行不通的。尝试使用另一个对象作为锁。
    2. 等待通知后需要运行。当两个线程都进入等待状态时,没有其他线程可以通知它们。

    试试这个代码:

    String word;
    Object a;
    public PingPong(String word, Object a) {
        this.word = word;
        this.a = a;
    }
    
    public void run() {
    
        synchronized (a) {
            for (int i = 0; i < 10; i++) {
                System.out.println(word + i);
                try {
    
                    a.notifyAll();
                    a.wait();
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
    
        Object a = new Object();
        Runnable p1 = new PingPong("ping", a);
        Thread t1 = new Thread(p1);
        t1.start();
    
        Runnable p2 = new PingPong("pong", a);
        Thread t2 = new Thread(p2);
        t2.start();
    
    }
    

    【讨论】:

    • 您的解决方案不支持先出现“ping”的顺序。没有保证(
    【解决方案2】:

    这是使用线程池执行器的类似解决方案:

    public class PingPong implements Runnable {
        String word;
        Lock lock;
    
        public PingPong(String word, Lock lock) {
            this.word = word;
            this.lock = lock;
        }
    
        @Override
        public void run() {
            while(true){
                System.out.println("Received : " + word);
                lock.notifyAll();
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            ExecutorService ex = Executors.newFixedThreadPool(2);
            Lock lock = new ReentrantLock();
            while(true){
                ex.submit(new PingPong("ping", lock));
                ex.submit(new PingPong("pong", lock));
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      以下解决方案基于:

      • Java 内部 API
      • 执行顺序

        public class Test {
        
        public static void main(String[] args) {
            SynchronousQueue<String> queue = new SynchronousQueue<>();
        
            Thread ping = new Thread(new Task(queue, "ping", "ping"));
            ping.setName("ping thread");
            ping.start();
        
            Thread pong = new Thread(new Task(queue, "pong", "ping"));
            pong.setName("pong thread");
            pong.start();
        
        
        }
        
        private static class Task implements Runnable {
            private SynchronousQueue<String> queue;
            private String command;
            private String step;
        
            Task(SynchronousQueue<String> queue, String command, String step) {
                this.queue = queue;
                this.command = command;
                this.step = step;
            }
        
            @Override
            public void run() {
                try {
                    if (command.equals(step)) {
                        doCommandAndWaitRepeatedly();
                    } else {
                        waitAndDoCommandRepeatedly();
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        
            private void doCommandAndWaitRepeatedly() throws InterruptedException {
                while (true) {
                    queue.offer(command, 1, TimeUnit.SECONDS);
                    Thread.sleep(500);
                    System.out.println(Thread.currentThread().getName() + ":" + queue.poll(1, TimeUnit.SECONDS));
                }
            }
        
            private void waitAndDoCommandRepeatedly() throws InterruptedException {
                while (true) {
                    System.out.println(Thread.currentThread().getName() + ":" + queue.poll(1, TimeUnit.SECONDS));
                    Thread.sleep(500);
                    queue.offer(command, 1, TimeUnit.SECONDS);
                }
            }
        }
        

        }

      【讨论】:

        【解决方案4】:
        class Ping extends Thread
        {
            public void run()
            {        
                for(int i=1;i<=5;i++)
                {
                    System.out.println("PING");
                    try{
                          sleep(2000);
                    }  catch(Exception e){}
                }       
             }
        }
        
        class Pong extends Thread
        {
            public void run() 
            {
                for (int i=1;i<=5;i++)
                {
                    System.out.println("PONG");
                    try{
                        sleep(2000);
                    }  catch(Exception e){}
                }
            }
        }
        
        public class PingPong
        {
            public static void main(String... args) throws Exception
            {
                Ping p1=new Ping();
                Pong p2=new Pong();
                p1.start();
                Thread.sleep(1000);  //super important for proper sequence
                p2.start();  
                p1.join();
            }
        }
        

        【讨论】:

        • 虽然此代码可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请edit您的答案添加解释并说明适用的限制和假设。
        猜你喜欢
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多