【问题标题】:Executing thread "one by one"执行线程“一个接一个”
【发布时间】:2011-12-18 22:17:52
【问题描述】:

有一个 n 线程可以访问的单例类。
每个线程都加载这个类的实例并循环调用这个类的一个方法。

我必须控制执行流程,以便每个线程都可以调用方法一并暂停,只有在所有线程调用该方法后,必须恢复它们的工作。 线程可以按任意顺序调用方法,只是它们都必须在移动循环之前执行一次方法。

这是我尝试做的:

在线程中:

 while ( some condition){
    ObjectType obj = theSingleton.getInstance().getSharedObject();
    obj.SomeMethod(threadID);
   if (obj.waitisneeded())
    synchronized (obj ) {
       obj.wait();
    }
}

我在 SomeMethod 中做了什么:

public  synchronized void SomeMethod(String threadID) {
 hashMap.put(threadID,true);
 some job here
}

需要等待:

public  synchronized boolean waitisneeded(){
{

   Iterator iter = hashMap.entrySet().iterator();

   boolean alldone = false;

   while (iter.hasNext()) { 
      Map.Entry me = (Map.Entry) iter.next();
      String key = me.getKey().toString();
      alldone = (Boolean)me.getValue();

      if(!alldone)  {
    return false;
       }
   }

  //set all values to false 
 iter = hashMap.entrySet().iterator();
 while (iter.hasNext()) { 
   Map.Entry me = (Map.Entry) iter.next();
    String key = me.getKey().toString();
   me.setValue(false);      
 }      
 this.notifyAll();
 return true;               

运行它给我带来了意想不到的结果和死锁。

我该如何解决?

注意:我不能改变线程的创建方式,我只能改变那些方法!!! (加上等待所在的线程中的while循环)

【问题讨论】:

  • 你知道在每个调用obj.SomeMethod()之前创建了多少个线程吗?
  • 你怎么知道“所有预期的线程现在都调用了单例”这一点已经达到了?
  • @TheScrumMeister Scrum Meister 是的,我知道线程数
  • @Thorbjørn Ravn Andersen,正如您在代码中看到的,在 getSharedObject 中有一个哈希图,我在其中标记了访问它的线程。当所有线程都检查时,我想释放线程并清除哈希图,以便它们可以重新开始。

标签: java multithreading thread-safety


【解决方案1】:

如果知道线程数,可以使用CyclicBarrier

static final CyclicBarrier barrier = new CyclicBarrier(numberOfThreads);

然后,当您想让所有线程等待其他线程时,您调用:

barrier.await();

如果有一个线程尚未到达该行,则调用await() 的线程将阻塞。一旦所有线程到达那里并调用await(),它们都会恢复。

每次你必须构建一些同步机制时,一定要检查包java.util.concurrent。那里有很多精彩的课程,由专家创建。大多数时候你不需要自定义的东西,如果你这样做了,同一个包中有一些类几乎不需要直接使用等待/通知——相信我,你想避免使用这些直接方法;如您所见,您很容易陷入僵局!

【讨论】:

  • 我认为 CyclicBarrier 存在问题,因为 var 是静态的。在我的应用程序中,这种情况可以并行发生多次! X 线程(我忘了提),生成 getSharedObject 并将其存储在单例的 hashmap 变量中。所以每组线程都访问自己的对象。这就是为什么我尝试使用锁,并将它自己作为 LOCK 的 getSharedObject!如果我使用静态,我将不得不为每个 getSharedObject 创建新变量
  • 好吧,我不知道您的应用程序是如何工作的。我相信你可以适应你的场景!整个想法是,在恢复之前必须相互等待的线程必须共享一个 CyclicBarrier。请务必正确发布障碍! (也就是说,由于您将拥有许多 CyclicBarrier,因此您最好使用并发集合来保存实例,例如 ConcurrentHashMap)。
【解决方案2】:

这是一种典型的多线程情况,称为 Rendez-Vous(法语单词,意为“约会”)。基本上,您的N 线程将有一个约会,并且都应该在某个时间点等待,直到所有线程都到达。正如@Bruno 提到的,您可以使用CyclicBarrier 对象来管理这种情况。如果您的方案只运行一次,那么您也可以使用CountDownLatch

以下是实现此目的的方法:

static final CountDownLatch rendezVousPoint = new CountDownLacth(numberOfThreads);

//Every thread does the following right before waiting on the rendez vous point
rendezVousPoint.countDown();
rendezVousPoint.await();

在这种情况下,每个线程都会阻塞await()方法,直到最后一个线程到达并释放所有线程(rendezVousPoint计数达到0)

【讨论】:

  • 我不确定我是否喜欢 CountDownLatch 的这种(ab)用法。您正在颠覆CountDownLatch,使其行为与CyclicBarrier 完全相同(专为处理这种情况而构建)。方法CDL.countDown()CDL.await(),就像你调用的那样,其行为与CB.await() 相似,但它们是有目的的两种方法(不是一个接一个地调用):通常,工作线程将调用@ 987654333@,只有一个主线程会调用CDL.await(),以便阻塞直到所有工作线程完成它们的任务。
  • @BrunoReis 感谢您的评论,我倾向于不同意您的意见。根据官方 java 文档,一个接一个地执行 CDL.countDown()CDL.await() 并没有错。 CyclicBarrier(根据文档)是CountDownLatch的可重复使用(可以设置和重置多次)版本
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
相关资源
最近更新 更多