一、前言:
本文主要讲解单例模式,单例模式分为两种,饿汉式和懒汉式。
1、概念:
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式特点:
- 为防止实例创建多个,将构造函数限制为 private;
- 建一个静态变量,持有一个自己的实例;
- 通过静态方式对外部提供这一实例;
2、饿汉式:
// 饿汉式单例
public class Singleton {
// 指向自己实例的私有静态引用,主动创建
private static Singleton instance = new Singleton();
// 私有的构造方法
private Singleton (){}
// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton getInstance() {
return instance;
}
}
我们知道,类加载的方式是按需加载,且加载一次。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,即能够充分保证单例。
3、饿汉式(静态内部类):
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
4、懒汉式(双重校验锁,最优方式):
使用双重检测同步延迟加载去创建单例的做法是一个非常优秀的做法,其不但保证了单例,而且切实提高了程序运行效率。对应的代码清单如下:
// 线程安全的懒汉式单例
public class Singleton {
//使用volatile关键字防止重排序,因为 new Instance()是一个非原子操作,可能创建一个不完整的实例
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
// 只需在第一次创建实例时才同步
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
如上述代码所示,为了在保证单例的前提下提高运行效率,我们需要对 singleton 进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同步获取锁了)。这种做法无疑是优秀的,但是我们必须注意一点:
必须使用volatile关键字修饰单例引用。
二、单例模式 的封装
public class ExecutorServiceCreator {
/**
* 私有构造
*/
private ExecutorServiceCreator() {
}
/**
* 静态内部类
*/
private static class FixedHolder{
private static final ExecutorService fixedExecutorService = newFixedThreadPool(8);
}
/**
* FixedThreadPool线程池
* @return
*/
public static ExecutorService getFixedInstance(){
return FixedHolder.fixedExecutorService;
}
/**
* 静态内部类
*/
private static class CachedHolder{
private static final ExecutorService cachedExecutorService = newCachedThreadPool();
}
/**
* CachedThreadPool线程池
* @return
*/
public static ExecutorService getCachedInstance(){
return CachedHolder.cachedExecutorService;
}
/**
* 静态内部类
*/
private static class ScheduledHolder{
private static final ExecutorService scheduledExecutorService = newCachedThreadPool();
}
/**
* CachedThreadPool线程池
* @return
*/
public static ExecutorService getScheduledInstance(){
return ScheduledHolder.scheduledExecutorService;
}
/**
* 静态内部类
*/
private static class SingletonHolder {
private static final ExecutorService singletonExecutorService = (ExecutorService) newCachedThreadPool();
}
/**
* CachedThreadPool线程池
* @return
*/
public static ExecutorService getSingleInstance() {
return SingletonHolder.singletonExecutorService;
}
}
采用静态内部类,进行单例封装,一个类中可以同时封装 n 个单例对象。
三、 总结
总之,从速度和反应时间角度来讲,饿汉式(又称立即加载)要好一些;从资源利用效率上说,懒汉式(又称延迟加载)要好一些。
单例模式的优点
-
在内存中只有一个对象,节省内存空间;
-
避免频繁的创建销毁对象,可以提高性能;
-
避免对共享资源的多重占用,简化访问;
-
为整个系统提供一个全局访问点。