import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
public class ZKApplication implements Watcher {
private static final int SESSION_TIMEOUT = 3000;
private volatile static boolean shutdown;
private ZooKeeper zk;
private CountDownLatch connectedSignal = new CountDownLatch(1);
public void connect(String hosts) throws IOException, InterruptedException {
try {
System.out.println("Start to create the Zookeeper instance");
zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);
new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while (count++ <= 1000) {
System.out.println(zk.getState());
try {
Thread.currentThread().sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
} catch(IOException e) {
e.printStackTrace();
throw e;
}
connectedSignal.await(); //Wait for the SyncConnected event occurs and process has been called, which will stop the waiting here. }
}
@Override
public void process(WatchedEvent event) { // Watcher interface
if (event.getState() == Event.KeeperState.SyncConnected) {
connectedSignal.countDown(); //connectedSignal.await() will no longer wait
}
}
public void create(String groupName) throws KeeperException,
InterruptedException {
String path = "/" + groupName;
String createdPath = zk.create(path, null/*data*/, ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL); //The znode will be deleted upon the session is closed.
System.out.println("Created " + createdPath);
}
public void close() throws InterruptedException {
zk.close();
}
public static void main(String[] args) throws Exception {
final ZKApplication createGroup = new ZKApplication();
String groupName = "zoo" + ThreadLocalRandom.current().nextInt();
createGroup.connect(Host.HOST);
createGroup.create(groupName);
createGroup.close();
}
}
上面的代码在Zookeeper没有启动的情况下,控制台输出1000次CONNECTING,也不退出,原因是已经发生了死锁问题。 引发死锁问题的是同步闭锁connectedSignal的使用,代码中的含义是客户端在connect方法中发起链接,然后connect一直等待直到 Watcher的process被回调将闭锁计数置零,通常这个没有问题,可是当Zookeeper压根没有启动的时候,这个代码会陷入死锁:
死锁发生在客户端阻塞等待于connectedSignal的await方法上,发生阻塞说明connectedSignal的计数没有置零,没有置零说明process没有调用,因为Zookeeper没有启动,所以说process没有被调用是合理的。代 码死锁于connectSignal的await方法上,那么意思是说,Zookeeper对象构造和会话建立过程应该是异步的,Zookeeper构造 方法返回后,另外一个会话创建线程会尝试建立会话,从代码的输出可以看出,会话状态一直是Connecting状态(试图创建会话,但尚未创建成功的状 态),Zookeeper的Javadoc清楚的说到Zookeeper实例和会话建立是异步的过程: Session establishment is asynchronous. This constructor will initiate connection to the server and return immediately - potentially (usually) before the session is fully established. The watcher argument specifies the watcher that will be notified of any changes in state. This notification can come at any point before or after the constructor call has returned.