-
什么是单例模式?
通过单例模式的方法创建的类在当前进程中只有一个实例
通俗来说就是:在程序的运行中,一个类从始至终被调用了很多次,但是只被实例过一次 -
单例模式-懒汉模式
懒汉模式又称为饱汉模式,指的是在程序执行中需要用到该类的时候才去进行实例//懒汉模式 public class Singleton { private static Singleton singleton = null; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { //需要用到该类的时候才去进行实例 singleton = new Singleton(); } return singleton; } }
优点:使用时才实例化,懒加载启动快,资源占用小
缺点:线程不安全-多个线程并发访问getInstance方法时singleton将被实例化多次针对于该缺点,有许多人直接给方法加上了synchronized关键字进行修饰,确实synchronized可以解决该线程不安全的缺点,然而又产生了另外一个缺点:性能低;因为synchronized为独占排他锁,当有多个线程同时访问getInstance方法时拿到执行权的只有一个线程,其他的线程则必须进行等待锁的释放。
所以出现了解决的方案:双重加锁检查DCL
public class Singleton { //使用volatile修饰让变量每次在使用的时候,都从主存中取,而不是从各个线程的"工作内存"中取 private volatile static Singleton instance = null; private Singleton(){ } public static Singleton getInstance(){ //判断是否已经实例过 if(instance == null){ //同步块,线程安全的创建实例 synchronized(Singleton.class){ //再次检查实例是否存在,如果不存在才创建实例 if(instance == null){ instance = new Singleton(); } } } return instance; } }
为什么需要在同步块中再次检查实例是否存在呢?
这个问题只需要明白如果去掉同步块中if会导致什么结果答案就呼之欲出了
当多线程并发进入第一个if(instance == null)时,此时的instance还未进行实例,遇到了synchronized同步块,只有A线程进入了同步块中,其他的线程都在同步块外面等待锁的释放,A线程进入后对instance进行了实例,这个时候再次进入getInstance方法的其他线程都会直接获得实例化完成的instance,然而与A线程同时进入getInstance方法的其他的线程B、C还在等待A线程释放synchronized同步块的锁,当A线程释放锁后,B线程进入同步块后会再次实例instance,C线程同理,所以就导致了instance还是会被多次实例,这时就解释了为什么需要在同步块中再次检查实例是否存在Double Check。
-
单例模式-饿汉模式
在程序初始化(如tomcat启动)的时候就进行实例public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return instance; } }
优点:绝对的线程安全,第一次使用的时候也无需初始化没有延迟
缺点:增加程序的启动时间,可能造成资源浪费(没用到该类) -
单例模式-内部类模式Holder(推荐)
改成内部类,由JVM保证线程安全性public class Singleton { //类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 private static class SingletonHolder{ private static Singleton instance = new Singleton(); } private Singleton(){ } public static Singleton getInstance(){ return SingletonHolder.instance; } }
优点:将懒加载和线程安全完美结合的一种方式
Java设计模式-多线程下的单例模式
最新推荐文章于 2023-05-09 19:04:07 发布