Java 多线程 线程互相等待 CyclicBarrier

这篇博客介绍了Java中CyclicBarrier的使用,通过它实现多线程间的等待和同步,确保所有线程完成各自任务后才能继续执行。CyclicBarrier在多线程竞赛场景中,让每个线程在到达终点前必须等待所有参赛者准备就绪,从而有序进行下一轮比赛。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先介绍一下JDK内容:

java.util.concurrent 
类 CyclicBarrier
java.lang.Object
  继承者 java.util.concurrent.CyclicBarrier

--------------------------------------------------------------------------------

public class CyclicBarrierextends Object一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。 

CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。 

示例用法:下面是一个在并行分解设计中使用 barrier 的例子: 

 class Solver {
   final int N;
   final float[][] data;
   final CyclicBarrier barrier;
   
   class Worker implements Runnable {
     int myRow;
     Worker(int row) { myRow = row; }
     public void run() {
       while (!done()) {
         processRow(myRow);

         try {
           barrier.await(); 
         } catch (InterruptedException ex) { 
return; 
         } catch (BrokenBarrierException ex) { 
return; 
         }
       }
     }
   }

   public Solver(float[][] matrix) {
     data = matrix;
     N = matrix.length;
     barrier = new CyclicBarrier(N, 
                                 new Runnable() {
                                   public void run() { 
                                     mergeRows(...); 
                                   }
                                 });
     for (int i = 0; i < N; ++i) 
       new Thread(new Worker(i)).start();

     waitUntilDone();
   }
 }
 在这个例子中,每个 worker 线程处理矩阵的一行,在处理完所有的行之前,该线程将一直在屏障处等待。处理完所有的行之后,将执行所提供的 Runnable 屏障操作,并合并这些行。如果合并者确定已经找到了一个解决方案,那么 done() 将返回 true,所有的 worker 线程都将终止。 
如果屏障操作在执行时不依赖于正挂起的线程,则线程组中的任何线程在获得释放时都能执行该操作。为方便此操作,每次调用 await() 都将返回能到达屏障处的线程的索引。然后,您可以选择哪个线程应该执行屏障操作,例如: 

  if (barrier.await() == 0) {
     // log the completion of this iteration
   }对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。 

内存一致性效果:线程中调用 await() 之前的操作 happen-before 那些是屏障操作的一部份的操作,后者依次 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。 



从以下版本开始: 
1.5 
另请参见:
CountDownLatch
然后是Java编程思想中的介绍:


import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * CyclicBarrier
 * 
 * CyclicBarrier适用于这样的情况:你希望创建一组任务,它们并行地执行工作,然后在进行下一个步骤之前等待,
 * 直到所有任务都完成(看起来有些像join())。它使得所有的并行任务都将在栅栏处列队,因此可以一直地向前移动。
 * 这非常像CountDownLatch,只是CountDownLatch是只触发一次的事件,而CyclicBarrier可以多次重用。
 * 
 * 对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:
 * 如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException
 * (如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。 
 * 内存一致性效果:线程中调用 await() 之前的操作 happen-before 那些是屏障操作的一部份的操作,
 * 后者依次 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。 
 * 
 * 下面是Hosrac的赛马游戏的面向对象的多线程版本,其中使用了CyclicBarrier
 * 
 */
/**
 * 马的对象,每匹马有自己的编号,每次前进将产生一个随机数的步数,前进一次后进行等待。
 * 
 * @create @author Henry @date 2017-1-3
 * 
 */
class Horse implements Runnable {
	private static int counter = 0;
	private final int id = counter++;
	private int strides = 0;
	private static Random rand = new Random(47);
	private static CyclicBarrier barrier;

	public Horse(CyclicBarrier barrier) {
		this.barrier = barrier;
	}

	public synchronized int getStrides() {
		return strides;
	}

	@Override
	public void run() {
		try {
			while (!Thread.interrupted()) {
				synchronized (this) {
					strides += rand.nextInt(3);
					if(getStrides()==14)
						throw new Exception("world");
				}
				barrier.await();
			}
		} catch (InterruptedException e) {
			System.err.println("InterruptedException");
		} catch (BrokenBarrierException e) {
			System.err.println("BrokenBarrierException");
			throw new RuntimeException(e);
		} catch (Exception e) {
			System.err.println("Exception");
			throw new RuntimeException(e);
		}
	}

	@Override
	public String toString() {
		return "Horse " + id + " ";
	}

	/**
	 * 输出马跑的步数。
	 * 
	 * @return
	 */
	public String tracks() {
		StringBuilder s = new StringBuilder();
		for (int i = 0; i < getStrides(); i++)
			s.append("*");
		s.append(id);
		return s.toString();
	}
}

/**
 * 马场跑道,定义长度为75。
 * 
 * @create @author Henry @date 2017-1-3
 * 
 */
public class HorseRace {
	static final int FINISH_LINE = 75;
	private List<Horse> horses = new ArrayList<Horse>();
	private ExecutorService exec = Executors.newCachedThreadPool();
	private CyclicBarrier barrier;

	public HorseRace(int nHorses, final int pause) {
		barrier = new CyclicBarrier(nHorses, new Runnable() {

			@Override
			public void run() {
				StringBuilder s = new StringBuilder();
				for (int i = 0; i < FINISH_LINE; i++)
					s.append("=");
				System.out.println(s.toString());
				for (Horse horse : horses)
					System.out.println(horse.tracks());
				for (Horse horse : horses)
					if (horse.getStrides() >= FINISH_LINE) {
						System.out.println(horse + " won!");
						exec.shutdownNow();
						return;
					}
				try {
					TimeUnit.MILLISECONDS.sleep(pause);
				} catch (InterruptedException e) {
					System.err.println("barrier-action sleep interrupted");
				}
			}
		});
		for (int i = 0; i < nHorses; i++) {
			Horse horse = new Horse(barrier);
			horses.add(horse);
			exec.execute(horse);
		}
	}

	public static void main(String[] args) {
		int nHorses = 8;
		int pause = 200;
		new HorseRace(nHorses, pause);
	}
}

    可以向CyclicBarrier提供一个“栅栏动作”,它是一个Runnable,当计数值到达0时自动执行---这是CyclicBarrier和CountDownLatch之间的另一个区别。这里,栅栏动作是作为匿名内部类创建的,它被提交给了CyclicBarrier的构造器。

    我试图让每匹马都打印自己,但是之后的显式顺序取决于任务管理器。CyclicBarrier使得每匹马都要执行为了向前移动所必需执行的所有工作,然后必须在栅栏处等待其他所有的马都准备完毕,当所有的马都向前移动的时候,CyclicBarrier将自动调用Runnale栅栏动作任务,按顺序显式马和终点线的位置。

    一旦所有的任务都越过了栅栏,它就会自动地为下一回合比赛做好准备。

    为了展示这个非常简单的动画效果,你需要将控制台视窗的尺寸调整为小到只有马时,才会展示出来。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值