请仔细看代码中的注释。
1.饿汉式
package mutithread.concurrent.chapter14;
/**
* 饿汉式
* @author zhangjinglong
* @date 2020-02-19-17:13
*
* 如果一个类中的成员属性比较少,且占用的内存资源不多,饿汉的方式未尝不可,相反,如果一个类中的成员都是比较重
* 的资源,那么这种方式就会有些不妥
* 饿汉式的单例模式可以保证多个线程下的唯一实例,getInstance方法性能比较高,但是无法进行懒加载
*/
//final不允许被继承
public final class Singleton {
//实例变量
private byte[] data=new byte[1024];
//在定义实例对象的时候直接初始化
private static Singleton instance=new Singleton();
//私有构造函数,不允许外部new
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
2.懒汉式
package mutithread.concurrent.chapter14;
/**
* 懒汉式
* @author zhangjinglong
* @date 2020-02-19-17:13
*
* 懒汉式的单例模式 不能 保证多个线程下的 单例的唯一性
*/
//final不允许被继承
public final class Singleton2 {
//实例变量
private byte[] data=new byte[1024];
//定义实例,但是不直接进行初始化
private static Singleton2 instance=null;
//私有构造函数,不允许外部new
private Singleton2(){
}
public static Singleton2 getInstance(){
//不能保证多线程下的单例唯一性
if(null==instance)
instance=new Singleton2();
return instance;
}
}
3.懒汉式+同步方法
package mutithread.concurrent.chapter14;
/**
* 懒汉式+同步方法
* @author zhangjinglong
* @date 2020-02-19-17:13
*
* 在懒汉式的基础上,增加同步的约束 可以 保证多个线程下的 单例的唯一性
* 但是synchronized关键字天生但排他性导致了getInstance方法只能在同一时刻被一个线程访问,性能低下
*/
//final不允许被继承
public final class Singleton3 {
//实例变量
private byte[] data=new byte[1024];
//定义实例,但是不直接进行初始化
private static Singleton3 instance=null;
//私有构造函数,不允许外部new
private Singleton3(){
}
//向getInstance方法加入同步控制,每次只能有一个线程能够进入
public static synchronized Singleton3 getInstance(){
if(null==instance)
instance=new Singleton3();
return instance;
}
}
4.Double-Check
提供了一种高效的数据同步策略
package mutithread.concurrent.chapter14;
import java.net.Socket;
import java.sql.Connection;
/**
* Double-Check 提供了一种高效的数据同步策略
* 在首次初始化时加锁,之后则允许多个线程同时进行getInstance方法的调用来获得类的实例
* @author zhangjinglong
* @date 2020-02-19-17:13
*
* 这种方式既满足了懒加载,又保证了instance实例的唯一性,提高了高效的数据同步策略,
* 可以允许多个线程同时对getInstance进行访问。
* 但是,这种方式在多线程的情况下有可能会引起空指针异常。
* 由于JVM运行时指令重排序和Happens-Before规则,instance,conn和socket三者之间的实例化关系并物前后关系的约束
* 如instance最先被实例化,而conn和socket并未完成实例化,
* 未完成实例化的实例调用其方法就会抛出空指针异常
*
*/
//final不允许被继承
public final class Singleton4 {
//实例变量
private byte[] data=new byte[1024];
//定义实例,但是不直接进行初始化
private static Singleton4 instance=null;
Connection conn;
Socket socket;
//私有构造函数,不允许外部new
private Singleton4(){
this.conn//初始化conn
this.socket//初始化socket
}
public static Singleton4 getInstance(){
//当instance 为null 时,进入同步代码块,同时该判断避免了每次都需要进入同步代码块,可以提高效率
if(null==instance){
//只有一个线程能够获得Singleton4.class关联的monitor
synchronized (Singleton4.class){
//判断如果instance 为null则创建
if(null==instance){
instance=new Singleton4();
}
}
}
return instance;
}
}
5.Volatile+Double-Check
为防止JVM在运行时指令重排序导致的问题,采用volatile关键字可以解决
package mutithread.concurrent.chapter14;
import java.net.Socket;
import java.sql.Connection;
/**
* Volatile+Double-Check
* @author zhangjinglong
* @date 2020-02-19-17:13
*
*
* * 由于JVM运行时指令重排序和Happens-Before规则,instance,conn和socket三者之间的实例化关系并物前后关系的约束
* * 如instance最先被实例化,而conn和socket并未完成实例化,
* * 未完成实例化的实例调用其方法就会抛出空指针异常
* 为防止JVM在运行时指令重排序导致的问题,采用volatile关键字可以满足
* 至此,多线程下的单例,懒加载以及获取实例的高效性都可满足
*/
//final不允许被继承
public final class Singleton5 {
//实例变量
private byte[] data=new byte[1024];
//定义实例,但是不直接进行初始化 注意使用volatile关键字保证conn和socket先实例化
private volatile static Singleton5 instance=null;
Connection conn;
Socket socket;
//私有构造函数,不允许外部new
private Singleton5(){
this.conn//初始化conn
this.socket//初始化socket
}
public static Singleton5 getInstance(){
//当instance 为null 时,进入同步代码块,同时该判断避免了每次都需要进入同步代码块,可以提高效率
if(null==instance){
//只有一个线程能够获得Singleton4.class关联的monitor
synchronized (Singleton5.class){
//判断如果instance 为null则创建
if(null==instance){
instance=new Singleton5();
}
}
}
return instance;
}
}
6.Holder方式
这种方式实现简单,原理复杂,仔细品。。
package mutithread.concurrent.chapter14;
import java.net.Socket;
import java.sql.Connection;
/**
*
* Holder方式
* 借助类加载的特点
* @author zhangjinglong
* @date 2020-02-19-17:13
*
* //在Single类中并没有instance的静态成员,而是将其放到了静态内部类Holder之中
* //因此在Singleton6类的初始化过程中并不会创建Singleton6的实例
* //Holder类中定义了Singleton6的静态变量,并且进行了实例化
* //当Holer被主动引用的时候则会创建Singleton6的实例
* //Singleton6实例的创建过程在JAVA程序编译时期收集至<clinit>()方法中,
* //该方法又是同步方法,同步方法可以保证内存的可见性、JVM指令的顺序性和原子性
* Holder方式的单例设计是最好的设计之一,也是目前使用比较广的设计之一
*/
//final不允许被继承
public final class Singleton6 {
//实例变量
private byte[] data=new byte[1024];
//私有构造函数,不允许外部new
private Singleton6(){
}
//在静态内部类中持有Singleton6的实例,并且可以被直接初始化
private static class Holder{
private static Singleton6 instance=new Singleton6();
}
//调用getInstance方法,事实上是获得Holder的instance静态属性
public static Singleton6 getInstance(){
return Holder.instance;
}
}
7.枚举方式
package mutithread.concurrent.chapter14;
/**
* 枚举方式 使用枚举类实现单例设计模式
* @author zhangjinglong
* @date 2020-02-19-20:44
*枚举类不能够懒加载,对Singleton主动使用
* 比如调用其中对静态方法则INSTANCE会立即得到实例化
*/
//枚举类型本身就是final的,不允许被继承
public enum Singleton7 {
INSTANCE;
//实例变量
private byte[] data=new byte[1024];
Singleton7(){
System.out.printf("INSTANCE will be initialized immediately");
}
public static void method(){
//调用该方法则会主动使用Single,INSTANCE将会被实例化
}
public static Singleton7 getInstance(){
return INSTANCE;
}
}
8.Holder方式+枚举
package mutithread.concurrent.chapter14;
/**
*
* 增加懒加载的特性,类似于Holder的方式
* @author zhangjinglong
* @date 2020-02-19-20:52
*/
public class Singleton8 {
//实例变量
private byte[] data=new byte[1024];
private Singleton8(){
}
//使用枚举冲淡holder
private enum EnumHolder{
INSTANCE;
private Singleton8 instance;
EnumHolder(){
this.instance=new Singleton8();
}
private Singleton8 getInstance(){
return instance;
}
}
public static Singleton8 getInstance(){
return EnumHolder.INSTANCE.getInstance();
}
}
小结
本文收纳了8种单例模式,
第5种Volatile+Double-Check方式,设计比较精巧,值得细品
其中第6种Holder方式的单例设计是最好的设计之一,也是目前使用比较广的设计之一。