一、什么是单例模式?
单例模式(Singleton)是一种比较常用的设计模式,在应用场景中,单例模式的类的对象有且只有一个。例如打印机只有一台,避免两个任务同时下发到打印机中,通信端口只有一个,避免多个请求同时调用通信端口,这样的情况下,单例模式是非常有用的。
单例模式就是确保一个类只有一个实例,并提供一个访问它的全局访问点。
类图 :
- Singleton() 构造方法私有化:防止外部初始化,由类本身进行实例化
- Singleton singleton 指向自己实例的私有静态引用
- getInstance() 以自己实例为返回值的静态的公有方法:对外提供获取实例的方法
二、模式详解
1、饿汉式:在类加载初始化的时候就主动创建实例,立即加载
package SingletonDemo;
//饿汉式
public class Singleton1 {
private Singleton1() {
}
//立即加载
private static Singleton1 singleton1 = new Singleton1();
public static Singleton1 getInstance() {
return singleton1;
}
}
2、懒汉式:等真正使用的时候再去创建实例,延迟实例化,不用时不主动创建实例
package SingletonDemo;
//懒汉式
public class Singleton2 {
private Singleton2() {
}
//类加载时不主动创建实例
private static Singleton2 singleton2;
//在要获取时候,真正使用时
public static Singleton2 getInstance() {
if (singleton2 == null) {
//再创建单例
singleton2 = new Singleton2();
}
return singleton2;
}
}
对比二者,饿汉式响应较快,懒汉式资源利用率上更高些,从线程安全的角度来说,饿汉式单例在线程访问单例类之前单例实例就已经创建完成了,不存在线程不安全这样的说法,懒汉式单例在线程访问单例类时,可能会多个线程进入判断单例为空的代码块,这样可能会创建多个线程,造成线程不安全的情况。
3、同步方法锁
package SingletonDemo;
//线程安全的懒汉式
public class Singleton3 {
private Singleton3() {
}
private static Singleton3 singleton3;
//同步方法锁,synchronized关键字修饰,临界资源的同步互斥
public static synchronized Singleton3 getInstance() {
if (singleton3 == null) {
singleton3 = new Singleton3();
}
return singleton3;
}
}
同步锁的颗粒度较大,效率不高,降低性能
4、同步代码块儿
package SingletonDemo;
//线程安全的懒汉式
public class Singleton4 {
private Singleton4() {
}
private static Singleton4 singleton4;
public static Singleton4 getInstance() {
//使用synchronized关键字修饰,临界资源同步互斥
synchronized ("") {
if (singleton4 == null) {
singleton4 = new Singleton4();
}
return singleton4;
}
}
}
同步锁的颗粒度有所减小,效率依然不高,性能依然不足
5、内部类
package SingletonDemo;
//线程安全的懒汉式
public class Singleton5 {
private Singleton5() {
}
//内部类
private static class Holder {
private static Singleton5 singleton5 = new Singleton5();
}
//调用getInstance才会去调用内部类,加载Singleton5实例
public static Singleton5 getInstance() {
return Holder.singleton5;
}
}
这种方式也是线程安全的,延时加载的懒汉式单例
6、双重检查(Double-Check)
package SingletonDemo;
//双重检查的懒汉式
public class Singleton6 {
private Singleton6() {
}
//volatile关键字防止重排序
private static volatile Singleton6 singleton6;
public static Singleton6 getInstance() {
//第一次检查
if (singleton6 == null) {
//确定为空之后在进入同步代码块,效率更高
synchronized ("") {
//只有第一次创建实例时才同步
if (singleton6 == null) {
singleton6 = new Singleton6();
}
}
}
return singleton6;
}
}
使用volatile关键字是为了保证操作可见性,新建单例实例时在JVM里面实际上分为多步,非原子操作,如果不加volatile关键字,可能会导致指令重排序
注:单例模式常被用于共享资源,如数据库连接池,线程池等场景