如何在Java中同步一组相对于单个线程的多个线程



假设我有一个名为myList的数组列表,其中所有线程都是用实现Runnable接口的myRunnable类的实例创建的,也就是说,所有线程共享在myRunnable的run()方法中执行的相同代码。现在假设我有另一个名为singleThread的线程,它是用实现Runnable接口的otherRunnable类的实例创建的。

我必须为这些线程解决的同步挑战如下:我需要myList中的所有线程执行它们的代码直到某个点。一旦达到这个程度,他们就应该睡觉了。一旦myList中的所有线程都处于睡眠状态,那么singleThread应该被唤醒(singleThread已经处于睡眠状态)。然后singleThread执行它自己的东西,当它完成后,它应该休眠,myList中的所有线程都应该被唤醒。假设代码被封装在while(true)语句中,那么这个过程必须一次又一次地进行。

下面是我刚刚描述的情况的一个例子,包括解决同步问题的尝试:

class myRunnable extends Runnable
{
  public static final Object lock = new Object();
  static int count = 0;
  @override
  run()
  { 
     while(true)
     {
        //do stuff
        barrier();
        //do stuff
     }
  }
  void barrier()
  {
     try {
       synchronized(lock) {
          count++;
          if (count == Program.myList.size()) {
             count = 0;
             synchronized(otherRunnable.lock) {           
                otherRunnable.lock.notify();
             }
          }
          lock.wait();
       }
     } catch (InterruptedException ex) {}
  }
}
class otherRunnable extend Runnable
{
   public static final Object lock = new Object();
   @override
   run()
   {
     while(true)
     {
       try {
         synchronized(lock) {
            lock.wait();
       } catch (InterruptedException ex) {}
       // do stuff
       try {
         synchronized(myRunnable.lock) {
           myRunnable.notifyAll();
       }
     }
   }        
}
class Program
{
  public static ArrayList<Thread> myList;
  public static void main (string[] args)
  {
     myList = new ArrayList<Thread>();
     for(int i = 0; i < 10; i++)
     {
        myList.add(new Thread(new myRunnable()));
        myList.get(i).start();
     }
     new Thread(new OtherRunnable()).start();
  }
}

基本上我的想法是使用一个计数器来确保线程在myList只是等待,除了最后一个线程增加计数器,它重置计数器为0,唤醒singleThread通知到它的锁,然后这个最后的线程去睡觉,以及等待myrunable .lock。在一个更抽象的层面上,我的方法是在myList中为线程使用某种屏障来停止它们在临界点的执行,然后最后一个线程击中屏障唤醒singleThread并进入睡眠状态,然后singleThread完成它的东西,当完成时,它唤醒屏障中的所有线程,以便它们可以再次继续。

我的问题是我的逻辑有缺陷(可能还有更多)。当最后一个线程到达barrier时通知otherRunnable。在最后一个线程执行对myRunnable的等待之前,有可能立即发生上下文切换,将cpu分配给singleThread。锁上(然后睡觉)。然后singleThread会执行它所有的东西,会在myRunnable上执行notifyAll。myList中的所有线程都将被唤醒,除了最后一个碰到barrier的线程,因为它还没有执行它的wait命令。然后,所有这些线程将再次执行它们的任务并再次遇到障碍,但是计数永远不会等于myList.size(),因为前面提到的最后一个线程最终将再次被调度并执行wait。singleThread反过来也会在它的第一行执行wait,结果我们有一个死锁,每个人都在睡觉。

所以我的问题是:什么是同步这些线程的好方法,以实现之前描述的预期行为,但同时以一种安全的死锁方式?

根据您的评论,听起来CyclicBarrier完全符合您的需求。来自文档(强调我的):

一个同步辅助允许一组线程都等待彼此到达一个公共屏障点。在涉及固定大小的线程的程序中,CyclicBarriers非常有用,这些线程必须偶尔相互等待。因为可以在等待的线程被释放之后被重用,所以这个barrier被称为循环的。

不幸的是,我自己没有使用过它们,所以我不能给你具体的指针。我认为基本的想法是,你用barrierAction的双参数构造函数来构造屏障。在此任务完成后,将n线程await()放在此barrier上,之后执行barrierAction,之后继续执行n线程。

From javadoc for CyclicBarrier#await():

如果当前线程是最后到达的线程,并且在构造函数中提供了一个非空的barrier动作,则当前线程在允许其他线程继续之前运行该动作。如果在barrier操作期间发生异常,则该异常将在当前线程中传播,并且barrier被置于断开状态。

最新更新