读最近同学发的一篇文章,单例模式最佳实践:https://www.jianshu.com/p/b08fdc6f86a1 有感。以前学过单例,没有实战经验,并不知道它在程序优化的方面有很大的作用。最近通过一段时间的学习和开发,对单例模式有了更多的了解。
----------不断学习才能跟上大神的脚步。
单例模式在高并发情况下还需要考虑很多东西,此篇只做自己参考
应用场景:
1、数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
2、多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
3、Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
4、Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
实例:(自己加上私有化的构造函数)
1、利用单例模式连接MongDB
public class MongoUtils {
private MongoUtils(){}
private static MongoDBDao componentDao = MongoUtils.getComponentMongDbDao();
public static MongoDBDao getComponentMongDbDao() {
// 获取mongo-component数据源,建立MongoDB连接池和MongoDBTemplate等封装的操作
return MongoDB.getMongoDBDao("mongo-component");
}
}
2、初始化redis集群工具类
public class GedisUtils {
private static GedisCommands gedis;
public static volatile GedisCommands getInstance() {
if (gedis != null) {
return gedis;
}
log.info("初始化Redis");
try {
synchronized (GedisUtils.class) {
if (gedis == null) {
if (EotBatchProperties.private_deploy) {
log.info("开始初始化单点Redis..addr=" + Properties.private_redis_addr);
gedis = GedisCommandsFactory.INSTANCE.getRedisCommand(Constant.PRIVATE_REDIS_TAG,
Properties.private_redis_addr);
} else {
log.info("开始初始化Redis集群");
GedisPoolConfig config = new GedisPoolConfig();
config.setMaxTotal(Properties.redis_conn_max_total);
gedis = GedisCommandsFactory.INSTANCE.getClusterCommand(Constant.PRIVATE_REDIS_TAG, "",
config);
}
}
}
} catch (Exception e) {
log.error("Gedis初始化过程出错,cause=" + ThrowableUtils.cause(e));
System.exit(1);
}
return gedis;
}
}
public enum GedisCommandsFactory {
INSTANCE;
private static ConcurrentHashMap<String, GedisCommands> singlePools = new ConcurrentHashMap();
public GedisCommands getRedisCommand(String tag, String id, GedisPoolConfig poolConfig) {
if (id != null && !id.trim().equals("")) {
String[] temp = id.trim().split(":");
if (temp.length != 3) {
RedisLog.info("非法的参数,id = " + id);
return null;
} else {
String key = GedisConstants.APPCODE + "-" + tag + "-" + id.trim();
GedisCommands proxy = (GedisCommands)singlePools.get(key);
if (proxy == null) {
proxy = GedisInitialize.getInstance().getSingleCommand(key, new GedisCommandsFactory.RedisConnInfo(tag, id, poolConfig));
if (proxy == null) {
return null;
}
//在此处可以保证单例
GedisCommands currentProxy = (GedisCommands)singlePools.putIfAbsent(key, proxy);
if (currentProxy != null) {
proxy = currentProxy;
}
}
return proxy;
}
} else {
RedisLog.info("获取redis连接池时id为空");
return null;
}
}
}
3、ZK的连接
public class ZkClientHolder {
private static CuratorFramework client = CuratorFrameworkFactory.builder().defaultData(null)
.connectString(ZkProperties.getZkAddress()).retryPolicy(new RetryNTimes(12, 5000)).build();
private static AtomicBoolean startup = new AtomicBoolean(false);
private static CountDownLatch countDownLatch = new CountDownLatch(1);
private ZkClientHolder(){}
public static CuratorFramework getInstance() {
return client;
}
public static void startup() {
//仅执行一次
if (startup.compareAndSet(false, true)) {
initZkClient();
}
}
private static void initZkClient() {
client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
if (newState.isConnected()) {
countDownLatch.countDown();
log.info("Zookeeper连接成功");
} else {
log.error("Zookeeper连接丢失,重连中...");
}
}
});
if (client.getState() == CuratorFrameworkState.LATENT) {
client.start();
//阻塞直接连接成功或超时
try {
if (!countDownLatch.await(60, TimeUnit.SECONDS)) {
// 创建连接失败,记录日志并退出应用
log.error("Zookeeper连接创建失败,超时,退出应用");
System.exit(1);
}
} catch (InterruptedException e) {
log.error("Zookeeper连接动作被中断,退出应用");
System.exit(1);
}
} else if (client.getState() == CuratorFrameworkState.STOPPED) {
log.error("Zookeeper连接创建失败,连接已关闭,退出应用");
System.exit(1);
}
}
}