面试题答案
一键面试基于Java线程安全设计模式实现跨节点单例服务
1. 双重检查锁定模式(DCL)基础
在Java中,经典的双重检查锁定模式实现单例如下:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile
关键字确保了instance
变量在多线程环境下的可见性,防止指令重排序导致在未完全初始化时被其他线程获取。
2. 跨节点单例服务设计
在分布式系统中,单纯的Java DCL无法直接实现跨节点单例,需要借助分布式协调工具,如Zookeeper。
实现步骤
- 利用Zookeeper创建唯一节点:
- 每个节点启动时,尝试在Zookeeper上创建一个特定路径的节点(如
/singleton_service
)。 - Zookeeper的特性保证只有一个节点能成功创建。
import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import java.util.concurrent.CountDownLatch; public class DistributedSingleton { private static final String ZK_SERVERS = "localhost:2181"; private static final String SINGLETON_PATH = "/singleton_service"; private static DistributedSingleton instance; private ZooKeeper zk; private CountDownLatch connectedSignal = new CountDownLatch(1); private DistributedSingleton() { try { zk = new ZooKeeper(ZK_SERVERS, 5000, new Watcher() { @Override public void process(WatchedEvent event) { if (event.getState() == Watcher.Event.KeeperState.SyncConnected) { connectedSignal.countDown(); } } }); connectedSignal.await(); } catch (Exception e) { e.printStackTrace(); } } public static DistributedSingleton getInstance() { if (instance == null) { synchronized (DistributedSingleton.class) { if (instance == null) { try { ZooKeeper zk = new ZooKeeper(ZK_SERVERS, 5000, null); Stat stat = zk.exists(SINGLETON_PATH, false); if (stat == null) { zk.create(SINGLETON_PATH, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); instance = new DistributedSingleton(); } else { // 等待已创建节点释放(可以通过Watcher监听节点删除事件) // 这里简单等待,实际应用中可优化 Thread.sleep(1000); getInstance(); } } catch (Exception e) { e.printStackTrace(); } } } } return instance; } }
- 每个节点启动时,尝试在Zookeeper上创建一个特定路径的节点(如
- 服务实例化:
- 创建成功的节点对应的服务实例化,其他节点等待该节点释放或获取其服务引用。
- 可以通过Zookeeper的节点数据存储服务实例的相关信息(如网络地址等),其他节点从该节点数据获取服务连接信息。
3. 实现过程中的挑战及解决方案
网络延迟
- 挑战:网络延迟可能导致Zookeeper创建节点请求响应缓慢,影响单例服务获取速度。
- 解决方案:设置合理的超时时间,在等待Zookeeper响应时避免无限期等待。同时,可以采用异步方式处理Zookeeper操作,减少对主线程的阻塞。
节点故障
- 挑战:持有单例服务的节点故障,可能导致单例服务不可用。
- 解决方案:利用Zookeeper的临时节点特性,当持有单例服务的节点故障时,其创建的临时节点自动删除。其他节点通过Watcher监听该节点删除事件,重新竞争创建单例节点,实现服务的高可用性。