什么是单例模式
保证某个类在程序中只存在一份实例,而不会创建多个实例,这样就会提高效率。
在单利模式中一般只提供一个getInstance()方法来获取实例对象,不提供setInstance()方法,目的是为了避免再实例化出其他实例对象。
其中单例模式中有两种模式一种是饿汉模式,一种是懒汉模式。
一.饿汉模式
1.饿汉模式的概念
饿汉模式就是在类加载的时候立刻会实例化,后续使用就只会出现一份实例。
2.饿汉模式代码
package thread.example; //饿汉模式 public class HungrySingle { //在类加载的时候就实例化了,类加载只有一次,所以值实例化出了一份该实例对象 private static HungrySingle instance = new HungrySingle(); public static HungrySingle getInstance() { return instance; } }
3.多线程是否线程安全
在类加载的时候就已经实例化了,所以该实例化没有涉及到实例化的修改操作,只是进行读取操作。在多线程情况下是线程安全的。
二.懒汉模式
1.懒汉模式的概念
在类加载的时候没有直接实例化,而是调用指定实例方法的时候再进行实例化,这样就能保证不想使用的时候也不会实例化。一般来说比饿汉模式的效率高。
2.单线程情况下的懒汉模式
package thread.example; //单线程的懒汉模式 public class LazySingle { private static LazySingle instance = null; //只有在调用该方法的时候才实例化 public static LazySingle getInstance() { if(instance == null) { instance = new LazySingle(); } return instance; } }
3.多线程情况下的懒汉模式
(1)导致懒汉模式在多线程情况下的不安全原因
在多线程的情况下,由于可能两个线程都会得到一份instance=null,这是因为如果线程1修改了自己县城中的instance后还没来得及修改主内存中的instance,所导致线程2也实例化出了一份instance对象,这时候也就不再是单例模式了。主要导致该问题的是由于这里面涉及到了对instance的修改操作,失去了原子性,为了保证原子性,我们想到了加锁,从而实现线程安全问题。
(2)解决方法代码示例
版本1
package thread.example; //多线程安全下的懒汉模式 public class LazySingle { private LazySingle() { } private static LazySingle instance = null; //只有在调用该方法的时候才实例化 public static synchronized LazySingle getInstance() { if (instance == null) { instance = new LazySingle(); } return instance; } }
版本1的代码虽然保证了线程安全,但是每次调用该方法时还是会出现加锁解锁问题,为了进一步优化,我们可以减小锁的粒度来提高效率,因为加了锁之后也就和高并发无缘了,但我们还是想提高效率,所以才会进行优化。
版本2
双重if判断加锁提高效率
package thread.example; public class SecurityLazyModle { private LazySingle() { } private static volatile SecurityLazyModle instance = null;//保证内存可见性,防止编译器过度优化(指令重排序) public static SecurityLazyModle getInstance() { if(instance == null) { synchronized (SecurityLazyModle.class) { if(instance == null) { instance = new SecurityLazyModle(); } } } return instance; } }
版本2的解释说明
第一层if是为了判断当前是否已经把实例创建出来,第二层synchronized是为了使进入当前if中的线程来竞争锁,当拿到锁的线程进入到第三层if之后判断是否为空,不为空就是实例化对象,然后再释放锁,释放锁之后,instance已经不为空了,后面的线程就被阻挡在了第三层if这里了,之后再来访问getInstance()方法,发现该instance已经不为空了,也就不用再抢占锁资源了,因为竞争锁也消耗大量的时间。通过这样处理,既保证了线程安全,也提高了效率。
这里使用volatile是为了防止编译器优化导致的指令重排序,在进行new一个对象不是原子性操作,可以分为三步骤:
1.分配内存空间
2.实例化对象
3.给变量赋值
对于上面的执行,如果1和3先执行了(假设2还没有完成),在第一层if外的线程这时候判断不为null,这时候就会直接返回该对象,但是这个对象只执行了一半,之后使用就会导致线程安全问题。
通过volatile就可以确保这3步骤必须执行完(无论顺序如何,最终都会执行完),外面的线程才可以执行,这时候就保证了该对象的完整性。
以上是Java单例模式的饿汉和懒汉模式实现方式详解的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

PhpStorm Mac 版本
最新(2018.2.1 )专业的PHP集成开发工具

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境