单例模式(饿汉式和懒汉式)
来源:互联网 发布:斯普特尼克恋人 知乎 编辑:程序博客网 时间:2024/06/09 20:10
以前学习单例的时候,只理解了简单部分。这次看DRP,对单例的饿汉式和懒汉式有了一些认识和对比。
在实际的开发中,有些地方需要一个类只有一个实例。比如:网站在线人数的计数器,再比如IDE中的工具箱之类的等等。当需要这个类只有一个实例时,我们就需要使用到单例模式。单例模式有两种实现方式:懒汉式(延迟加载)和 饿汉式(预加载)。
目前遇到的情况使用饿汉式的比较多,也因为它比较简单。代码:
public class ClientManager { private static ClientManager instance = new ClientManager(); //静态私有成员,已初始化public ClientManager(){}public static ClientManager getInstance(){ //静态,不用同步(类加载时已初始化,不会有多线程的问题)return instance;}}饿汉式:在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。这里getInstance()是static的,不用同步(类加载时已初始化,不会有多线程的问题)
饿汉式程序运行过程中会节省时间,但是实例不管有没有用到都会占用空间。在这方面懒汉式似乎比饿汉式优化。我们先看看代码:
public class ClientManager {private static ClientManager instance = null;private ClientManager(){}public static ClientManager getInstance(){ //静态,同步,公开访问点if(instance == null){return new ClientManager();}return instance;}}比较懒,在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢。这里getInstance()是static的。我们来想象一下:要使用ClientManager,直接调用类的getInstance()方法。第一次的时候发现instance是null,然后就新建一个对象,返回出去;第二次再使用的时候,因为这个instance是static的,所以已经不是null了,因此不会再创建对象,直接将其返回。那么为什么有同步的问题?
线程A希望使用ClientManager,调用getInstance()方法。因为是第一次调用,A就发现instance是null的,于是它开始创建实例,就在这个时候,CPU发生时间片切换,线程B开始执行,它要使用ClientManager,调用getInstance()方法,同样检测到instance是null——注意,这是在A检测完之后切换的,也就是说A并没有来得及创建对象——因此B开始创建。B创建完成后,切换到A继续执行,因为它已经检测完了,所以A不会再检测一遍,它会直接创建对象。这样,线程A和B各自拥有一个ClientManager的对象——单例失败!
对于这种情况,我们很容易就想到加锁来解决。那么加锁后是怎么样的?
public class ClientManager{ private static ClientManager instance = null; public synchronized static ClientManager getInstance() { //给方法加锁 if(instance == null) { instance = new ClientManager(); } return instance; } private ClientManager() { } }getInstance()加上同步锁,一个线程必须等待另外一个线程创建完成后才能使用这个方法,这就保证了单例的唯一性。但是还有出现这样一个性能问题:每次调用getInstants时都需要加锁,会降低运行速度。所以我们还可以进一步改进。
public class ClientManager { private static ClientManager instance = null; public static ClientManager getInstance() { if (instance == null) { synchronized (ClientManager.class) { if(instance == null) { instance = new ClientManager(); } } } return instance; } private ClientManager() { }这样的方法可以双重锁定。我们一般用让线程每次都加锁,而只是实例未被创建的时候再加锁处理,同时也能保证多线程的安全。为什么要进行两次instance==null的判断,这个交给大家自己想一下吧。就按照上面线程A和线程B这样的方式。
最后,总结一下饿汉式和懒汉式的区别:饿汉式类加载时已初始化,不会有多线程的问题,使用简单。懒汉式是在需要时才对类进行实例化,但是有多线程问题,需要该考虑怎么加锁的问题。
0 0
- 单例模式-饿汉式和懒汉式
- 单例模式(懒汉式和饿汉式)
- 单例模式(懒汉式和饿汉式)
- 单例模式(懒汉式和饿汉式)
- 单例模式(饿汉式和懒汉式)
- java单例模式(懒汉式和饿汉式)
- 单例模式(饿汉式和懒汉式)
- 单例模式(懒汉式和饿汉式)
- 单例设计模式(饿汉式和懒汉式)
- 单例模式(饿汉式&懒汉式)
- 设计模式-单例模式之饿汉式和懒汉式
- 设计模式------单例模式之饿汉式和懒汉式
- 单例模式两种模式--饿汉式和懒汉式
- 单例模式的懒汉式和饿汉式
- Java单例模式:饿汉式和懒汉式的实现
- 单例模式的懒汉式和饿汉式
- Java单例模式:饿汉式和懒汉式的实现
- Java单例模式:饿汉式和懒汉式的实现
- Python 写Excel
- 第十四周项目四:电子词典
- linux 创建usr/java 并复制文件到这个文件夹
- 第十四周项目六:阅读程序
- Nim游戏博弈(收集完全版)
- 单例模式(饿汉式和懒汉式)
- jQuery三种事件绑定方式:bind(),.live(),.delegate()
- 牛人网站和博客---站得高看得更远(CV类)
- PhotoShop算法实现--色调/饱和度调整(四)
- python 回调函数和回调方法的实现
- 意图——学习笔记总结
- 移动端 video 的autoplay不起作用的解决方案
- LeetCode - Minimum Window Substring 题解
- setinel分布式部署+redis主从集群+java客户端连接资料整理分享