设计模式-单例模式
某个对象全局只需要一个实例时,就可以使用单例模式。
开始学习单例的各种写法
第一种:简单懒汉式模式
/** * 第一种 * 简单懒汉式 */ public class Singleton { /** * 单例模式不能被别人new 所以访问权限改为私有。必须的 */ private Singleton(){} //懒汉式 不会先去new对象 而是在用的时候才回去创建 所以叫懒汉式 private static Singleton singleton =null; //因为为全局唯一访问点 所以需要定义为static,属性也定义为static public static Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
public class SingletonTest {
public static void main(String[] args) {
//测试简单懒汉式
//Singleton singleton = new Singleton(); 方法私有不能创建
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1);
Singleton instance2 = Singleton.getInstance();
System.out.println(instance2);
Singleton instance3 = Singleton.getInstance();
System.out.println(instance3);
}
}
运行结果:
com.shangjun.test02.Singleton@4554617c com.shangjun.test02.Singleton@4554617c com.shangjun.test02.Singleton@4554617c
该模式有缺点,同一个线程中不会有问题,但在多线程的情况下会出现线程安全问题。
测试多线程情况
public class Singleton { /** * 单例模式不能被别人new 所以访问权限改为私有。必须的 */ private Singleton(){} //懒汉式 不会先去new对象 而是在用的时候才回去创建 所以叫懒汉式 private static Singleton singleton =null; //因为为全局唯一访问点 所以需要定义为static,属性也定义为static public static Singleton getInstance(){ if(singleton==null){ try { Thread.sleep(10);//添加睡眠时间 时间越长更明显 } catch (InterruptedException e) { e.printStackTrace(); } singleton = new Singleton(); } return singleton; } }
public class SingletonTest {
public static void main(String[] args) {
//测试多线程模式下懒汉式
for(int i=0;i<5;i++){
new Thread(()->{
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1);
}).start();
}
}
}
//运行结果
com.shangjun.test02.Singleton@1b9c704e
com.shangjun.test02.Singleton@2ceb2de
com.shangjun.test02.Singleton@67c084e5
com.shangjun.test02.Singleton@47e3e4b5
com.shangjun.test02.Singleton@ae526cf
第二种:简单懒汉升级版
简单懒汉模式会有线程安全问题,所以要对访问资源进行加锁 访问方法添加synchronized关键字。
/** * 懒汉式 */ public class Singleton { /** * 单例模式不能被别人new 所以访问权限改为私有。必须的 */ private Singleton(){} //懒汉式 不会先去new对象 而是在用的时候才回去创建 所以叫懒汉式 //volatile 添加该关键字可以防止指令重排序 保证线程修改被其他线程知道, private volatile static Singleton singleton =null; //因为为全局唯一访问点 所以需要定义为static,属性也定义为static public synchronized static Singleton getInstance(){ if(singleton==null){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } singleton = new Singleton(); } return singleton; } }
public class SingletonTest {
public static void main(String[] args) {
//测试多线程模式下懒汉式
for(int i=0;i<5;i++){
new Thread(()->{
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1);
}).start();
}
}
}
测试结果:
com.shangjun.test02.Singleton@2fd04fd1
com.shangjun.test02.Singleton@2fd04fd1
com.shangjun.test02.Singleton@2fd04fd1
com.shangjun.test02.Singleton@2fd04fd1
com.shangjun.test02.Singleton@2fd04fd1
懒汉式这种模式可以在使用的时候才建立对象,但是因为加锁的缘故,所以当并发比较多的时候会导致性能问题。因为每次访问该方法会等锁释放后才能够获得对象。
第三种:饿汉式模式
与懒汉式相对应的就是饿汉式,与懒汉式对比,在调用之前对象已经建立。不需要进行加锁,也就不存在性能问题。
/** * 饿汉式 */ public class Singleton { /** * 单例模式不能被别人new 所以访问权限改为私有。必须的 */ private Singleton(){} //饿汉式 不管用不用,直接创建对象 private static Singleton singleton = new Singleton(); //因为为全局唯一访问点 所以需要定义为static,属性也定义为static public static Singleton getInstance(){ return singleton; } } public class SingletonTest { public static void main(String[] args) { //测试多线程模式下饿汉式 for(int i=0;i<5;i++){ new Thread(()->{ Singleton instance1 = Singleton.getInstance(); System.out.println(instance1); }).start(); } } }
测试结果:
com.shangjun.test02.Singleton@79a19a06
com.shangjun.test02.Singleton@79a19a06
com.shangjun.test02.Singleton@79a19a06
com.shangjun.test02.Singleton@79a19a06
com.shangjun.test02.Singleton@79a19a06
但是这种情况会直接创建好对象,启动的时候会耗费内存,耗费时间,所以有了第四种。
第四种:双重检锁机制(double check locking 简称DCL)
可以进行懒加载,相当于懒汉式的加强版 进行两次判断 一次加锁完成 ,注意属性添加 volatile 防止指令重排序
/** * 双重检索机制 DCL */ public class Singleton { /** * 单例模式不能被别人new 所以访问权限改为私有。必须的 */ private Singleton(){} //懒汉式 不会先去new对象 而是在用的时候才回去创建 所以叫懒汉式 //volatile 添加该关键字可以防止指令重排序 保证线程修改被其他线程知道 private volatile static Singleton singleton = null; //因为为全局唯一访问点 所以需要定义为static,属性也定义为static public static Singleton getInstance(){ //第一次进行检查判断 if(singleton==null){ //进行加锁 synchronized (Singleton.class){ //第二次进行检查判断 if(singleton==null){ //创建对象 singleton = new Singleton(); } } } return singleton; } } public class SingletonTest { public static void main(String[] args) { //测试多线程模式下饿汉式 for(int i=0;i<5;i++){ new Thread(()->{ Singleton instance1 = Singleton.getInstance(); System.out.println(instance1); }).start(); } } }
运行结果:
com.shangjun.test02.Singleton@14550b42
com.shangjun.test02.Singleton@14550b42
com.shangjun.test02.Singleton@14550b42
com.shangjun.test02.Singleton@14550b42
com.shangjun.test02.Singleton@14550b42
第五种:静态内部类
/** * 静态内部类方式 */ public class Singleton { /** * 单例模式不能被别人new 所以访问权限改为私有。必须的 */ private Singleton(){} private static class Holder{ //volatile 添加该关键字可以防止指令重排序 保证线程修改被其他线程知道 private volatile static Singleton singleton = new Singleton(); } //因为为全局唯一访问点 所以需要定义为static,属性也定义为static public static Singleton getInstance(){ return Holder.singleton; } } public class SingletonTest { public static void main(String[] args) { //测试多线程模式下饿汉式 for(int i=0;i<5;i++){ new Thread(()->{ Singleton instance1 = Singleton.getInstance(); System.out.println(instance1); }).start(); } } }
运行结果:
com.shangjun.test02.Singleton@47e3e4b5
com.shangjun.test02.Singleton@47e3e4b5
com.shangjun.test02.Singleton@47e3e4b5
com.shangjun.test02.Singleton@47e3e4b5
com.shangjun.test02.Singleton@47e3e4b5
我们把Singleton实例放到一个静态内部类中,这样可以避免了静态实例在Singleton类的加载阶段(类加载过程的其中一个阶段的,此时只创建了Class对象,静态变量初始化是在Singleton类初始化时触发的,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的。
第六种:枚举方式
public class Singleton { } public enum SingletonEnum { INSTANCE; private Singleton Singleton =new Singleton(); public Singleton getInstance(){ return Singleton; } } public class SingletonTest { public static void main(String[] args) { //测试多线程模式下饿汉式 for(int i=0;i<5;i++){ new Thread(()->{ // Singleton instance1 = Singleton.getInstance(); System.out.println(SingletonEnum.INSTANCE.getInstance()); }).start(); } } }
测试结果:
com.shangjun.test02.Singleton@1b9c704e
com.shangjun.test02.Singleton@1b9c704e
com.shangjun.test02.Singleton@1b9c704e
com.shangjun.test02.Singleton@1b9c704e
com.shangjun.test02.Singleton@1b9c704e
关注微信公众号: 程序员子期 获取更多技能

浙公网安备 33010602011771号