【问题标题】:Thread Synchronization - Synchronizing three threads to print 012012012012..... not working线程同步 - 同步三个线程以打印 012012012012 ..... 不工作
【发布时间】:2014-07-23 13:44:09
【问题描述】:

我正在尝试同步三个线程以打印 012012012012.... 但它无法正常工作。每个线程都分配有一个编号,当它从主线程接收到信号时会打印该编号。以下程序有问题,我无法捕捉到。

public class Application {
    public static void main(String[] args) {

        int totalThreads = 3;
        Thread[] threads = new Thread[totalThreads];

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new MyThread(i);
            threads[i].start();
        }

        int threadIndex = 0;
        while (true) {
            synchronized(threads[threadIndex]) {
                threads[threadIndex].notify();
            }

            threadIndex++;
            if (threadIndex == totalThreads) {
                threadIndex = 0;
            }
        }
    }
}

class MyThread extends Thread {
    private int i;

    public MyThread(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        while (true) {
            synchronized(this) {
                waitForSignal();
                System.out.println(i);
            }
        }
    }

    private void waitForSignal() {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

【问题讨论】:

  • 请定义什么是“有问题”...
  • 它没有按要求的顺序打印数字。对于其中一次运行,它打印了 01212012102....
  • 您没有在非实例信号量上进行同步。例如尝试使用静态成员变量
  • 因为这感觉有点像家庭作业,所以在提出解决方案时我们应该遵守哪些规则?
  • 不,没有规则。

标签: java multithreading thread-synchronization


【解决方案1】:

你需要更多的协调。 notify 调用不会立即唤醒线程并强制它继续。相反,将notify 视为向线程发送电子邮件以使其知道它可以继续。想象一下,如果你想让你的 3 个朋友按顺序给你打电话。你给朋友 1 发了一封电子邮件给你打电话,等了一秒钟,给朋友 2 发了一封电子邮件,等了一秒钟,然后给朋友 3 发了一封电子邮件。你认为你会按照这个确切的顺序被打电话吗?

增加更多协调的一种方法是拥有一些共享状态,表明轮到谁了。如果你所有的朋友都能看到你的房子,你可以在房子外面放一个数字,表明轮到谁打电话了。每个朋友都会等到他们看到他们的号码,然后打电话。

【讨论】:

    【解决方案2】:

    这是你的问题:

        int threadIndex = 0;
        while (true) {
            synchronized(threads[threadIndex]) {
                threads[threadIndex].notify();
            }
    
            threadIndex++;
            if (threadIndex == totalThreads) {
                threadIndex = 0;
            }
        }
    

    主线程以正确的顺序通知所有线程。但是,您的线程是独立工作的。他们可能会或可能不会被安排在特定的时间点。所以最终结果可能是,线程 2 在线程 1 之前在线程 0 之前到达wait/print 锁。最终顺序不是由您发送通知决定的,而是(本质上)由调度程序决定。

    解决办法就是这样改

    1. 主线程只通知一个线程:线程 0
    2. 每个线程都完成自己的工作,完成后通知行中的下一个线程
    3. 显然最后一个线程必须再次通知线程0。

    【讨论】:

      【解决方案3】:

      另一种可能的解决方案:在主线程中,您可以在通知线程后立即等待(在同一个synchronized 块中),如下所示:

      synchronized (threads[threadIndex])
      {
          threads[threadIndex].notify();
          threads[threadIndex].wait(); // try/catch here
      }
      

      并且在线程的run方法中,可以在线程完成工作后使用notifyAll唤醒主线程:

      synchronized (this)
      {
          waitForSignal();
          System.out.println(i);
          notifyAll();
      }
      

      更复杂的解决方案将涉及 java.util.concurrent.locks 包中的类。

      【讨论】:

      • 我使用了你建议的解决方案,现在它陷入僵局。您是否尝试过解决方案?
      • @AnandPatel 当然。它不应该陷入僵局。当调用“wait”方法时,实际的锁被释放。很高兴在死锁时看到堆栈跟踪(使用 jVisualVM 或调试器创建),因为我无法想象它应该挂在哪里。如果这不可能,我会删除答案(看看问题是否可以通过其他方式解决)
      【解决方案4】:
      package threads;
      
      import java.util.concurrent.Semaphore;
      
      public class ZeroEvenOddPrinter {
         class Runner extends Thread{
             Semaphore prev;
             Semaphore next;
             int num = 0;
             public Runner(Semaphore prev,Semaphore next,int num){
                 this.prev = prev;
                 this.next = next;
                 this.num = num;
             }
             @Override
             public void run(){
                  while (true) {
                      try {
                          Thread.sleep(100);
                          prev.acquire();
                      } catch (InterruptedException e) {
                          // TODO Auto-generated catch block
                          e.printStackTrace();
                      }
      
                      if (num == 0)
                          System.out.println(0);
                      else {
                          System.out.println(num);
                          num = num + 2;
                      }
                      next.release();
                  }
              }
         }
         static public void main(String args[]) throws InterruptedException{
             Semaphore sem1 = new Semaphore(1);
             Semaphore sem2 = new Semaphore(1);
             Semaphore sem3 = new Semaphore(1);
             ZeroEvenOddPrinter zeo = new ZeroEvenOddPrinter();
             Runner t1 = zeo.new Runner(sem1,sem2,0);
             Runner t2 = zeo.new Runner(sem2,sem3,1);
             Runner t3 = zeo.new Runner(sem3,sem1,2);
             sem1.acquire();
             sem2.acquire();
             sem3.acquire();
             t1.start();
             t2.start();
             t3.start();
             sem1.release();
         }
      }
      

      这里我使用信号量作为所有三个线程的触发器。最初,所有线程都将在 sem1、sem2、sem3 上被阻塞。然后我将释放 sem1,第一个线程将执行,然后它将释放第二个线程,依此类推......最好的部分是你将此逻辑扩展到 n 个线程。祝你好运!!!

      【讨论】:

        猜你喜欢
        • 2014-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-25
        • 1970-01-01
        • 2019-11-05
        • 1970-01-01
        • 2012-02-19
        相关资源
        最近更新 更多