java单例模式,单例模式的作用及创建方法
java单例模式,单例模式的作用及创建方法详细介绍
本文目录一览: Java程序性能优化-单例模式(1)
单例模式( )
单例模式是设计模式中使用最为普遍的模式之一 它是一种对象创建模式 用于产生一个对象的具体实例 它可以确保系统中一个类只产生一个实例 在Java语言中 这样的行为能带来两大好处
( )对于频繁使用的对象 可以省略创建对象所花费的时间 这对于那些重量级对象而言 是非常可观的一笔系统开销
( )由于new操作的次数减少 因而对系统内存的使用频率也会降低 这将减轻GC压力 缩短GC停顿时间
因此对于系统的关键组件和被频繁使用的对象 使用单例模式便可以有效地改善系统的性能
单例模式的参与者非常简单 只有单例类和使用者两个 如表 所示
表 单例模式角色
它的基本结构如图 所示
图 单例模式类图
单例模式的核心在于通过一个接口返回唯一的对象实例 一个简单的单例实现如下
public class Singleton {
private Singleton(){
System out println( Singleton is create ) //创建单例的过程可能会比较慢
}
private static Singleton instance = new Singleton()
public static Singleton getInstance() {
return instance;
}
}
注意代码中的重点标注部分 首先单例类必须要有一个private访问级别的构造函数 只有这样 才能确保单例不会在系统中的其他代码内被实例化 这点是相当重要的 其次 instance成员变量和getInstance()方法必须是static的
注意 单例模式是非常常用的一种结构 几乎所有的系统中都可以找到它的身影 因此 希望读者可以通过本节 了解单例模式的几种实现方式及其各自的特点
这种单例的实现方式非常简单 而且十分可靠 它唯一的不足仅是无法对instance实例做延迟加载 假如单例的创建过程很慢 而由于instance成员变量是static定义的 因此在JVM加载单例类时 单例对象就会被建立 如果此时 这个单例类在系统中还扮演其他角色 那么在任何使用这个单例类的地方都会初始化这个单例变量 而不管是否会被用到 比如单例类作为String工厂 用于创建一些字符串(该类既用于创建单例Singleton 又用于创建String对象)
public class Singleton {
private Singleton() {
System out println( Singleton is create )
//创建单例的过程可能会比较慢
}
private static Singleton instance = new Singleton()
public static Singleton getInstance() {
return instance;
}
public static void createString(){ //这是模拟单例类扮演其他角色
System out println( createString in Singleton )
}
}
返回目录 Java程序性能优化 让你的Java程序更快 更稳定
编辑推荐
Java程序设计培训视频教程
J EE高级框架实战培训视频教程
J ME移动开发实战教学视频
Visual C++音频/视频技术开发与实战
Oracle索引技术
lishixinzhi/Article/program/Java/gj/201311/27837
java之单例设计模式
设计模式 解决某一类问题最行之有效的方法 (java中有 种通用设计模式) 单例设计模式 解决一个类在内存中只存在一个对象 单例设计模式有两种方式 )饿汉式 先初始化对象 当类一进内存就创建好对象 )懒汉式 对象是方法被调用时才初始化 也叫延时加载 类进内存时 对象还没有存在 只有通过方法调用时 才建立对象 单例设计模式 是想要保证对象的唯一 )为了避免其他程序过多建立该类对象 先禁止其他程序建立该类对象 )还为了让其他程序可以访问到该类对象 只好在本类中自定义一个对象 )为了方便其他程序对自定义对象的访问 可以对外其他一些访问方式 利用这三步进行代码实现 )将构造函数私有化 )在类中创建一个本类对象 )提供一个方法可以获取到该对象 提示 在单例设计模式中 事物该怎么描述还怎么描述 当需要将该事物的对象保证在内存中唯一时 加上以上三步即可 代码说明 // )饿汉式 [java] class Single { private int num; public void setNum(int num) { this num = num; } public int getNum() { return num; } //将构造函数私有化 private Single(){} //在类中创建一个本类对象 因为构造函数被私有化 该类不能创建对象 想在外部访问 所以将其静态 private static Single s = new Single() //提供一个方法可以获取到该对象 不能创建对象 所以将方法静态 可以通过 (类名 方法名)的形式进行访问 public static Single getInstance() { return s; } } class SingleDemo { public static void main(String[] args) { //单例的构造函数被静态后不能创建对象 保证对象的唯一 所以通过 (类名 方法名)的形式进行访问 Single s = Single getInstance() Single s = Single getInstance() s setNum( ) System out println(s getNum()) //结果 因为Single单例里面对对象和方法进行了静态 数据共享 所以s 的值 } }
lishixinzhi/Article/program/Java/hx/201311/26060
Java模式设计之单例模式(一)
作为对象的创建模式[GOF ] 单例模式确保某一个类只有一个实例 而且自行实例化并向整个系统提供这个实例 这个类称为单例类
单例模式的要点
单例单例
显然单例模式的要点有三个 一是某各类只能有一个实例 二是它必须自行创建这个事例 三是它必须自行向整个系统提供这个实例 在下面的对象图中 有一个 单例对象 而 客户甲 客户乙 和 客户丙 是单例对象的三个客户对象 可以看到 所有的客户对象共享一个单例对象 而且从单例对象到自身的连接线可以看出 单例对象持有对自己的引用
资源管理
一些资源管理器常常设计成单例模式
在计算机系统中 需要管理的资源包括软件外部资源 譬如每台计算机可以有若干个打印机 但只能有一个Printer Spooler 以避免两个打印作业同时输出到打印机中 每台计算机可以有若干传真卡 但是只应该有一个软件负责管理传真卡 以避免出现两份传真作业同时传到传真卡中的情况 每台计算机可以有若干通信端口 系统应当集中管理这些通信端口 以避免一个通信端口同时被两个请求同时调用
需要管理的资源包括软件内部资源 譬如 大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置 这样的系统应当由一个对象来管理一个属性文件
需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件 记录软件系统内部事件 出错信息的部件 或是对系统的表现进行检查的部件等 这些部件都必须集中管理 不可政出多头
这些资源管理器构件必须只有一个实例 这是其一 它们必须自行初始化 这是其二 允许整个系统访问自己这是其三 因此 它们都满足单例模式的条件 是单例模式的应用
一个例子 Windows 回收站
Windows x 以后的视窗系统中都有一个回收站 下图就显示了Windows 的回收站
在整个视窗系统中 回收站只能有一个实例 整个系统都使用这个惟一的实例 而且回收站自行提供自己的实例 因此 回收站是单例模式的应用
双重检查成例
在本章最后的附录里研究了双重检查成例 双重检查成例与单例模式并无直接的关系 但是由于很多C 语言设计师在单例模式里面使用双重检查成例 所以这一做法也被很多Java 设计师所模仿 因此 本书在附录里提醒读者 双重检查成例在Java 语言里并不能成立 详情请见本章的附录
单例模式的结构
单例模式有以下的特点
…… 单例类只可有一个实例
…… 单例类必须自己创建自己这惟一的实例
…… 单例类必须给所有其他对象提供这一实例
虽然单例模式中的单例类被限定只能有一个实例 但是单例模式和单例类可以很容易被推广到任意且有限多个实例的情况 这时候称它为多例模式(Multiton Pattern) 和多例类(Multiton Class) 请见 专题 多例(Multiton )模式与多语言支持 一章 单例类的简略类图如下所示
由于Java 语言的特点 使得单例模式在Java 语言的实现上有自己的特点 这些特点主要表现在单例类如何将自己实例化上
饿汉式单例类饿汉式单例类是在Java 语言里实现得最为简便的单例类 下面所示的类图描述了一个饿汉式单例类的典型实现
从图中可以看出 此类已经自已将自己实例化
代码清单 饿汉式单例类
public class EagerSingleton { private static final EagerSingleton m_instance = new EagerSingleton() /** * 私有的默认构造子*/ private EagerSingleton() { } /** * 静态工厂方法*/ public static EagerSingleton getInstance()
{
Java 与模式return m_instance }
读者可以看出 在这个类被加载时 静态变量m_instance 会被初始化 此时类的私有构造子会被调用 这时候 单例类的惟一实例就被创建出来了
Java 语言中单例类的一个最重要的特点是类的构造子是私有的 从而避免外界利用构造子直接创建出任意多的实例 值得指出的是 由于构造子是私有的 因此 此类不能被继承
懒汉式单例类
与饿汉式单例类相同之处是 类的构造子是私有的 与饿汉式单例类不同的是 懒汉式单例类在第一次被引用时将自己实例化 如果加载器是静态的 那么在懒汉式单例类被加载时不会将自己实例化 如下图所示 类图中给出了一个典型的饿汉式单例类实现
代码清单 懒汉式单例类
package javapatterns singleton demos public class LazySingleton { private static LazySingleton m_instance = null /** * 私有的默认构造子 保证外界无法直接实例化*/ private LazySingleton() { } /** * 静态工厂方法 返还此类的惟一实例*/ synchronized public static LazySingleton getInstance()
{ if (m_instance == null)
{ m_instance = new LazySingleton() } return m_instance }
读者可能会注意到 在上面给出懒汉式单例类实现里对静态工厂方法使用了同步化 以处理多线程环境 有些设计师在这里建议使用所谓的 双重检查成例 必须指出的是 双重检查成例 不可以在Java 语言中使用 不十分熟悉的读者 可以看看后面给出的小节
同样 由于构造子是私有的 因此 此类不能被继承 饿汉式单例类在自己被加载时就将自己实例化 即便加载器是静态的 在饿汉式单例类被加载时仍会将自己实例化 单从资源利用效率角度来讲 这个比懒汉式单例类稍差些
从速度和反应时间角度来讲 则比懒汉式单例类稍好些 然而 懒汉式单例类在实例化时 必须处理好在多个线程同时首次引用此类时的访问限制问题 特别是当单例类作为资源控制器 在实例化时必然涉及资源初始化 而资源初始化很有可能耗费时间 这意味着出现多线程同时首次引用此类的机率变得较大
饿汉式单例类可以在Java 语言内实现 但不易在C++ 内实现 因为静态初始化在C++ 里没有固定的顺序 因而静态的m_instance 变量的初始化与类的加载顺序没有保证 可能会出问题 这就是为什么GoF 在提出单例类的概念时 举的例子是懒汉式的 他们的书影响之大 以致Java 语言中单例类的例子也大多是懒汉式的 实际上 本书认为饿汉式单例类更符合Java 语言本身的特点
登记式单例类
登记式单例类是GoF 为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的 本书把他们的例子翻译为Java 语言 并将它自己实例化的方式从懒汉式改为饿汉式 只是它的子类实例化的方式只能是懒汉式的 这是无法改变的 如下图所示是登记式单例类的一个例子 图中的关系线表明 此类已将自己实例化
代码清单 登记式单例类
import java util HashMap public class RegSingleton { static private HashMap m_registry = new HashMap() static { RegSingleton x = new RegSingleton() m_registry put( x getClass() getName() x) } /** * 保护的默认构造子*/ protected RegSingleton() {} /** * 静态工厂方法 返还此类惟一的实例*/ static public RegSingleton getInstance(String name)
{ if (name == null)
{ name = javapatterns singleton demos RegSingleton } if (m_registry get(name) == null)
{ try { m_registry put( name Class forName(name) newInstance() ) } catch(Exception e)
{ System out println( Error happened ) } return (RegSingleton) (m_registry get(name) ) } /** * 一个示意性的商业方法*/ public String about()
{ return Hello I am RegSingleton }它的子类RegSingletonChild 需要父类的帮助才能实例化 下图所示是登记式单例类子类的一个例子 图中的关系表明 此类是由父类将子类实例化的
下面是子类的源代码
代码清单 登记式单例类的子类
import java util HashMap public class RegSingletonChild extends RegSingleton { public RegSingletonChild() {} /** * 静态工厂方法*/ static public RegSingletonChild getInstance()
{ return (RegSingletonChild)
RegSingleton getInstance( javapatterns singleton demos RegSingletonChild ) } /** * 一个示意性的商业方法*/ public String about()
{ return Hello I am RegSingletonChild }
在GoF 原始的例子中 并没有getInstance() 方法 这样得到子类必须调用的getInstance(String name)方法并传入子类的名字 因此很不方便 本章在登记式单例类子类的例子里 加入了getInstance() 方法 这样做的好处是RegSingletonChild 可以通过这个方法 返还自已的实例 而这样做的缺点是 由于数据类型不同 无法在RegSingleton 提供这样一个方法 由于子类必须允许父类以构造子调用产生实例 因此 它的构造子必须是公开的 这样一来 就等于允许了以这样方式产生实例而不在父类的登记中 这是登记式单例类的一个缺点
lishixinzhi/Article/program/Java/gj/201311/27416
java开发在什么情况下使用单例模式?
使用原则如下:
1.单例模式:确保一个类只有一个实例,自行实例化并向系统提供这个实例
2.单例模式分类:饿单例模式(类加载时实例化一个对象给自己的引用),懒单例模式(调用取得实例的方法如getInstance时才会实例化对象)(java中饿单例模式性能优于懒单例模式,c++中一般使用懒单例模式)
单例模式(Singleton),也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
如何写一个简单的单例模式?
简单的单例模式如下
[java]view plaincopyprint?
public class Singleton{
private static Singleton unique Instance=null;
private Singleton(){
//Exists only to defeat instantiation.
}
public static Singleton getInstance(){
if(unique Instance==null){
unique Instance=new Singleton();
}
return unique Instance;
}
//Other methods...
}
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。
每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
总之,选择单例模式就是为了避免不一致状态,避免政出多头。
扩展资料一、单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
二、单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
1.饿汉式单例类
[java]view plaincopyprint?
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1{
//私有的默认构造子
private Singleton1(){}
//已经自行实例化
private static final Singleton1 single=new Singleton1();
//静态工厂方法
public static Singleton1 getInstance(){
return single;
}
}
2.懒汉式单例类
[java]view plaincopyprint?
//懒汉式单例类.在第一次调用的时候实例化
public class Singleton2{
//私有的默认构造子
private Singleton2(){}
//注意,这里没有final
private static Singleton2 single=null;
//静态工厂方法
public synchronized static Singleton2 getInstance(){
if(single==null){
single=new Singleton2();
}
return single;
}
}
3.登记式单例类
[java]view plaincopyprint?
import java.util.HashMap;
import java.util.Map;
//登记式单例类.
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3{
private static Map
map=new HashMap
();
static{
Singleton3 single=new Singleton3();
map.put(single.getClass().getName(),single);
}
//保护的默认构造子
protected Singleton3(){}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(Stringname){
if(name==null){
name=Singleton3.class.getName();
System.out.println("name==null"+"--->name="+name);
}
if(map.get(name)==null){
try{
map.put(name,(Singleton3)Class.forName(name).newInstance());
}catch(InstantiationExceptione){
e.printStackTrace();
}catch(IllegalAccessExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}
}
returnmap.get(name);
}
//一个示意性的商业方法
public String about(){
return"Hello,IamRegSingleton.";
}
public static void main(String[]args){
Singleton3 single3=Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
参考资料:百度百科——java单例模式
java 单例模式这个要怎么理解?
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
介绍
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
应用实例:
1、一个班级只有一个班主任。
2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
单例模式的作用及创建方法
单例模式作为常见的设计模式之一,在java的项目开发中会时常的用到。Java Singleton模式即保证在JVM运行时,一个类Class只有一个实例存在。
单例模式有什么好处呢? 最简单的一个例子就是网站计数器的设计了。当我们想要统计当前网站的在线人数时,一个显而易见的问题就是并发所带来的线程安全问题,当我们对这个计数器(网站人数)在同一时刻进行操作,再保存计数时就会造成数据的混乱,后者覆盖前者的结果。一种解决方案就是把这个计数器设置为唯一对象,所有人都必须共用同一份数据。 实现唯一对象最好的解决办法就是让类自己负责保存它的唯一实例,并且让这个类保证不会产生第二个实例,同时提供一个让外部对象访问该实例的方法。自己的事情自己办,而不是由别人代办,这非常符合面向对象的封装原则。
单例模式的三个特点:
只有在自身需要的时候才会行动,从来不知道及早做好准备。它在需要对象的时候,才判断是否已有对象,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返回。
该方法在多线程情况下有可能重复创建实例,以下是线程安全的懒汉模式
这种模式的缺点是加锁造成了效率下降,并且在绝大部分情况下是不需要同步的。使用双重检验锁(DCL),只在第一次初始化的时候进行同步加锁
该方式在类加载的时候就被实例化了。
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉不同的是(很细微的差别):饿汉方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
想象一下,如果实例化instance很消耗资源,我想让它延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式就显得很合理。
关于类加载情况下单例模式,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。修复的办法是
单例模式(四)
本篇主要介绍几种常用的写法,还是以Java语言为例,其中会涉及到Java语言的特性,如果用其他语言来写单例模式,主要还是抓住单例的全局唯一性原则去考虑即可。如果想看往期单例模式的,可以从这些链接进入:
单例模式(一)
单例模式(二)
单例模式(三)
关于单例模式,其实网上也早有流传一种叫法,分别是懒汉模式和饿汉模式,所谓懒汉模式即懒加载,也就是等到用的时候再去创建单例对象,而饿汉模式即立即创建单例对象,下面来介绍几种常用的几种单例写法:包括2种懒汉模式 + 2种饿汉模式:
第一种:饿汉模式
在 Singleton 类初始化的时候就创建了单例对象singleton,这也是饿汉模式的命名的原因;这种模式在单例模式(一)的时候我们也分析过。
第二种:懒汉模式
这种模式就是在用的时候,才创建单例对象,关于两次的校验 if (singleton == null )和 synchronized 主要是起到了多线程安全和优化性能的作用,在单例模式(二)和 单例模式(三)中也仔细分析过。
第三种:懒汉模式 ,运用java内部类的写法
这种写法其实跟语言特性有关,内部类也是java中一种特性写法,不一定每种语言都有,所以当用其他语言来写的时候,要注意语言特性问题。
这种写法,就是在 Singleton 类中加了一个 SingletonHolder 类,这就是内部类,所谓的内部类,就是一个大类中还有一个小类,只有当调用到 getInstance 方法的时候,才会去加载 SingletonHolder 这个类( Java类加载原理 ,后面会专门有篇章介绍类加载),这样就能满足调用时再创建对象的条件了,另外由于类加载是线程安全的,所以这里也不会有多线程竞争条件存在,也符合了全局唯一性原则。
第四种:饿汉模式 ,运用java 枚举类enum的写法
枚举enum也是java语言的一个特性,跟上面的内部类一样,具体选择语言的时候,还是要根据语言特性去选择。
这种方式也是单例模式的最简写法,也是《Effective Java》作者Josh Bloch推荐的写法,既能保证多线程安全,也能保证单例对象全局唯一性。
其实写到这里似乎意犹未尽,时间不多,今天就先列举单例模式常用的4种写法,下一篇会继续介绍一些遗留的问题,比如:
等等问题,会在下一篇继续讨论,如果有其他没有涵盖到的问题或者疑问,也欢迎读者一起提问讨论。
设计模式之单例模式
本文开始整个设计模式的系列学习,希望通过不断的学习,可以对设计模式有整体的掌握,并在项目中根据实际的情况加以利用。
单例模式是指一个类仅允许创建其自身的一个实例,并提供对该实例的访问权限。它包含静态变量,可以容纳其自身的唯一和私有实例。它被应用于这种场景——用户希望类的实例被约束为一个对象。在需要单个对象来协调整个系统时,它会很有帮助。
1、单例类只能有一个实例
2、单例类必须自己创建自己的唯一实例
3、单例类必须给其他所有对象提供这一实例
1.尽量使用懒加载
2.双重检索实现线程安全
3.构造方法为private
4.定义静态的Singleton instance对象和getInstance()方法
单例模式至少有六种写法。
作为一种重要的设计模式,单例模式的好处有:
1、控制资源的使用,通过线程同步来控制资源的并发访问
2、控制实例的产生,以达到节约资源的目的
3、控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。但其实通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。
虽然也是只有一个线程能够执行,假如线程B先执行,线程B获得锁,线程B执行完之后,线程 A获得锁,但是此时没有检查singleton是否为空就直接执行了,所以还会出现两个singleton实例的情况。
既然懒汉式是非线程安全的,那就要改进它。最直接的想法是,给getInstance方法加锁不就好了,但是我们不需要给方法全部加锁啊,只需要给方法的一部分加锁就好了。基于这个考虑,引入了双检锁(Double Check Lock,简称DCL)的写法:
使用volatile 的原因:
对于JVM而言,它执行的是一个个Java指令。在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间, 然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就使出错成为了可能,我们仍然以A、B两个线程为例:
加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
枚举类实现单例模式是 effective java 作者极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。因为枚举类没有构造方法,可以防止反序列化操作。
1、除枚举方式外, 其他方法都会通过反射的方式破坏单例,反射是通过调用构造方法生成新的对象,所以如果我们想要阻止单例破坏,可以在构造方法中进行判断,若已有实例, 则阻止生成新的实例,解决办法如下:
2、如果单例类实现了序列化接口Serializable, 就可以通过反序列化破坏单例,所以我们可以不实现序列化接口,如果非得实现序列化接口,可以重写反序列化方法readResolve(), 反序列化时直接返回相关单例对象。
Runtime是一个典型的例子,看下JDK API对于这个类的解释"每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接,可以通过getRuntime方法获取当前运行时。应用程序不能创建自己的Runtime类实例。",这段话,有两点很重要:
1、每个应用程序都有一个Runtime类实例
2、应用程序不能创建自己的Runtime类实例
只有一个、不能自己创建,是不是典型的单例模式?看一下,Runtime类的写法:
为了节约系统资源,有时需要确保系统中某个类只有唯一一个实例,当这个唯一实例创建成功之后,我们无法再创建一个同类型的其他对象,所有的操作都只能基于这个唯一实例。为了确保对象的唯一性,我们可以通过单例模式来实现。
单例模式应用的场景一般发现在以下条件下:
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
关于单例模式的漫画分析: https://mp.weixin.qq.com/s/f-sJIZHr7JUa31gKTllSFQ
单例模式的优缺点、注意事项、使用场景