分布式Barrier是这样一个类: 它会阻塞所有节点上的等待进程,知道某一个被满足, 然后所有的节点继续进行。
比如赛马比赛中, 等赛马陆续来到起跑线前。 一声令下,所有的赛马都飞奔而出。
1.栅栏Barrier
1.DistributedBarrier类说明
DistributedBarrier类实现了栅栏的功能。它的构造函数如下:
/**
* @param client client
* @param barrierPath path to use as the barrier
*/
public DistributedBarrier(CuratorFramework client, String barrierPath)
DistributedBarrier构造函数中 barrierPath 参数用来确定 一个栅栏,只要 barrierPath 参数相同(路径相同)就是同一个栅栏。通常情况下栅栏的使用如下:
1.主导client设置一个栅栏
2.其他客户端就会调用 waitOnBarrier()等待栅栏移除,程序处理线程阻塞
3. 主导client移除栅栏, 其他客户端的处理程序就会同时继续运行。
DistributedBarrier类的主要方法如下:
- setBarrier() - 设置栅栏
- waitOnBarrier() - 等待栅栏移除
- removeBarrier() - 移除栅栏
异常处理: DistributedBarrier 会监控连接状态,当连接断掉时waitOnBarrier()方法会抛出异常。
2.编写示例程序
public class DistributedBarrierExample
{
private static final int QTY = 5;
private static final String PATH = "/examples/barrier";
public static void main(String[] args) throws Exception
{
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
client.start();
ExecutorService service = Executors.newFixedThreadPool(QTY);
DistributedBarrier controlBarrier = new DistributedBarrier(client, PATH);
controlBarrier.setBarrier();
for (int i = 0; i < QTY; ++i)
{
final DistributedBarrier barrier = new DistributedBarrier(client, PATH);
final int index = i;
Callable<Void> task = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
Thread.sleep((long) (3 * Math.random()));
System.out.println("Client #" + index + " 等待");
barrier.waitOnBarrier();
System.out.println("Client #" + index + " 开始");
return null;
}
};
service.submit(task);
}
Thread.sleep(1000 * 3);
System.out.println("所有的Client都在等待");
controlBarrier.removeBarrier();
service.shutdown();
service.awaitTermination(10, TimeUnit.MINUTES);
client.close();
System.out.println("OK!");
}
}
这个例子创建了 controlBarrier 来设置栅栏和移除栅栏。 我们创建了5个线程,在此Barrier上等待。 最后移除栅栏后所有的线程才继续执行。
如果你开始不设置栅栏,所有的线程就不会阻塞住。
3.示例程序运行结果
运行结果控制台:
Client #1 等待
Client #2 等待
Client #0 等待
Client #4 等待
Client #3 等待
所有的Client都在等待
Client #4 开始
Client #2 开始
Client #0 开始
Client #3 开始
Client #1 开始
OK!
运行时查看Zookeeper节点信息如下:
2.双栅栏Double Barrier
双栅栏允许客户端在计算的开始和结束时同步。当足够的进程加入到双栅栏时,进程开始计算,当计算完成时,离开栅栏。 双栅栏类是DistributedDoubleBarrier
1. DistributedDoubleBarrier类说明
DistributedDoubleBarrier类实现了双栅栏的功能。它的构造函数如下:
// client - the client
// barrierPath - path to use
// memberQty - the number of members in the barrier
public DistributedDoubleBarrier(CuratorFramework client, String barrierPath, int memberQty)
memberQty是成员数量,当enter方法被调用时,成员被阻塞,直到所有的成员都调用了enter。当leave方法被调用时,它也阻塞调用线程,知道所有的成员都调用了leave。
就像百米赛跑比赛, 发令枪响, 所有的运动员开始跑,等所有的运动员跑过终点线,比赛才结束。
注意:参数 memberQty 的值只是一个阈值,而不是一个限制值。当等待栅栏的数量大于或等于这个值栅栏就会打开!
与栅栏( DistributedBarrier )一样,双栅栏的 barrierPath 参数也是用来确定是否是同一个栅栏的, 双栅栏的使用情况如下:
1.从多个客户端在同一个路径上创建双栅栏( DistributedDoubleBarrier ),然后调用enter()方法,等待栅栏数量达到 memberQty 时就可以进入栅栏。
2. 栅栏数量达到 memberQty ,多个客户端同时停止阻塞继续运行,直到执行leave()方法,等待 memberQty 个数量的栅栏同时阻塞到 leave()方法中。
3. memberQty 个数量的栅栏同时阻塞到 leave()方法中, 多个客户端的 leave()方法停止阻塞,继续运行。
DistributedDoubleBarrier类的主要方法如下:
- enter()、enter(long maxWait, TimeUnit unit) - 等待同时进入栅栏
- leave()、leave(long maxWait, TimeUnit unit) - 等待同时离开栅栏
异常处理: DistributedDoubleBarrier 会监控连接状态,当连接断掉时enter()和leave方法会抛出异常。
2.编写示例程序
public class DistributedBarrierDoubleExample
{
private static final int QTY = 5;
private static final String PATH = "/examples/barrier";
public static void main(String[] args) throws Exception
{
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
client.start();
ExecutorService service = Executors.newFixedThreadPool(QTY);
for (int i = 0; i < (QTY + 2); ++i)
{
final DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client, PATH, QTY);
final int index = i;
Callable<Void> task = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
Thread.sleep((long) (3 * Math.random()));
System.out.println("Client #" + index + " 等待");
if(false == barrier.enter(5, TimeUnit.SECONDS))
{
System.out.println("Client #" + index + " 等待超时!");
return null;
}
System.out.println("Client #" + index + " 进入");
Thread.sleep((long) (3000 * Math.random()));
barrier.leave();
System.out.println("Client #" + index + " 结束");
return null;
}
};
service.submit(task);
}
service.shutdown();
service.awaitTermination(10, TimeUnit.MINUTES);
client.close();
System.out.println("OK!");
}
}
注意:创建双栅栏的数量为: ( QTY + 2 ) ,而创建双栅栏的参数为: new DistributedDoubleBarrier ( client , PATH , QTY ) , 当等待栅栏的数量大于或等于这个值( QTY )栅栏就会打开!
3.示例程序运行结果
运行结果控制台:
Client #0 等待
Client #2 等待
Client #3 等待
Client #4 等待
Client #1 等待
Client #4 进入
Client #2 进入
Client #0 进入
Client #1 进入
Client #3 进入
Client #4 结束
Client #5 等待
Client #2 结束
Client #3 结束
Client #6 等待
Client #0 结束
Client #1 结束
Client #5 等待超时!
Client #6 等待超时!
OK!
运行时查看Zookeeper节点信息如下: