- Barriers解决什么问题?
当有些操作需要所有参与者全部准备好之后才能开始执行,并且对每个参与者来说必须等待所有参与者全部执行完毕,才算执行完毕。于是就需要一个屏障,来控制所有参与者同时开始,并等待所有参与者全部结束。
- 实现barriers需要注意的问题
1.如何控制所有线程同时开始?
- 参与者准备好时就在/barrier父节点下插入顺序临时节点。
- 调用getChidren方法,并设置watch,检查/barrier下所有children节点的数量是否等于所有参与者的数量。
- 如果等于,则说明所有参与者都已到达,本参与者开始执行自己的业务代码。
- 如果不是,则此参与者调用wait()方法阻塞。
- 当有别的参与者插入新的子节点时这个参与者会收到通知,调用notify唤醒线程,第二步重新开始执行。
2.如何等待所有线程结束?
- 参与者执行完毕后,调用delete方法删除自己的子节点
- 获取父节点下的子节点数量n,并设置watch
- 如果n=0,说明所有参与者都已执行完毕,本参与者是最后一个,则可以直接结束。
- 如果n>0,则本参与者调用wait方法阻塞
- 如果别的参与者执行完毕,删除了一个子节点,则本参与者会收到通知,调用notify唤醒线程,第二步重新开始执行。
3.用什么类型的节点?
根节点使用持久节点(persistent node),子节点使用临时节点(Ephemeral node)
根节点为什么要用持久节点?首先因为临时节点不能有子节点,所以根节点要用持久节点,并且在程序中要判断根节点是否存在。
子节点为什么要用临时节点?临时节点随着连接的断开而消失,在程序中,虽然会删除临时节点,但可能会出现程序在节点被删除之前就crash了,如果是持久节点,节点不会被删除。
- 源码
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
//这个类是barrier的父类,负责初始化zkclient,并且实现了Watcher接口,负责接收zookeeper通知
public class SyncPrimitive implements Watcher {
static ZooKeeper zk = null;//zk client对象
static Integer mutex; //这是barrier用到的锁
String root;//这是barrieer父节点
SyncPrimitive(String address) {
if(zk == null){
try {
System.out.println("Starting ZK:");
zk = new ZooKeeper(address, 3000, this);
mutex = new Integer(-1);
System.out.println("Finished starting ZK: " + zk);
} catch (IOException e) {
System.out.println(e.toString());
zk = null;
}
}
//else mutex = new Integer(-1);
}
//接收zookeeper通知,唤醒barrier中的enter()和leave()方法中的等待线程
synchronized public void process(WatchedEvent event) {
synchronized (mutex) {
//System.out.println("Process: " + event.getType());
mutex.notify();
}
}
/**
* Barrier
*/
static public class Barrier extends SyncPrimitive {
int size;
String name;//本参与者对应的子节点path
/**
* Barrier constructor,调用父类构造函数,并创建父节点
*
* @param address
* @param root
* @param size
*/
Barrier(String address, String root, int size) {
super(address);
this.root = root;
this.size = size;
// Create barrier node
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
System.out
.println("Keeper exception when instantiating queue: "
+ e.toString());
} catch (InterruptedException e) {
System.out.println("Interrupted exception");
}
}
// My node name
try {
name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
} catch (UnknownHostException e) {
System.out.println(e.toString());
}
}
/**
* Join barrier 参与者执行enter进入屏障
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
boolean enter() throws KeeperException, InterruptedException{
zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true);
if (list.size() < size) {
mutex.wait();
} else {
return true;
}
}
}
}
/**
* Wait until all reach barrier 参与者执行leave()离开屏障
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
boolean leave() throws KeeperException, InterruptedException{
zk.delete(root + "/" + name, 0);
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true);
if (list.size() > 0) {
mutex.wait();
} else {
return true;
}
}
}
}
}