1. 单例模式动机
在我们的设计的系统中某些类来讲,只有一个实例是很重要的。例如一个系统中存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或者文件系统;一个系统只能有一个计时工具或ID序号生成器。
2. 单例模式的定义
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,并且它需要提供全局访问的方法。
英文定义
Ensure a class has only one instance and provide a global point of access to it.
3. 单例模式结构分析
单例模式三个要素
- 私有化构造器,使得外部无法通过new关键词直接创建实例
- 声明一个私有静态的自身类成员属性
- 提供一个公共的获取该类实例的静态方法,提供全局访问
4. 单例模式之饿汉模式
/**
* 单例模式之饿汉模式
* @author li
*/
public class IdentitlyCradNoHungry {
private final static IdentitlyCradNoHungry instance = new IdentitlyCradNoHungry();
private final String NO = "No001";
private IdentitlyCradNoHungry(){}
public static IdentitlyCradNoHungry getInstance(){
return instance;
}
public String getIdentitlyCradNo(){
return NO;
}
}
饿汉模式:为什么称其为饿汉呢?原因就是该方式在声明自身类的私有静态对象时便直接给其实例化,也就是我们在将此类加载到内存中时,就算我们并没有对该实例进行使用,也会因为初始化的原因而直接将对象实例化。
优点是:该方式是线程安全的,也就是无需考虑对并发带来的不确定性因素
缺点是:将加载时间放在了初始化阶段
5. 单例模式之懒汉模式-不加锁
/**
* 单例模式之懒汉模式 朴素版本
* @author li
*
*/
public class IdentitlyCradNoLazySimple {
private static IdentitlyCradNoLazySimple instance;
private String no;
private IdentitlyCradNoLazySimple(){}
public static IdentitlyCradNoLazySimple getInstance(){
if(instance == null){
instance = new IdentitlyCradNoLazySimple();
instance.setIdentitlyCradNo("No001");
System.out.println("第一次办理身份证!");
}else{
System.out.println("重复办理身份证!");
}
return instance;
}
private void setIdentitlyCradNo(String no){
this.no = no;
}
public String getIdentitlyCradNoLazySimple(){
return no;
}
}
懒汉模式普通版本:该方式并没有直接在类对象定义时直接对其实例化,而是在公开的实例获取方法中加入了判断条件,如果当前的对象为null,那么我们就对其通过new关键词来实例化对象。此时的方法会存在的问题是:当我们引入多线程时, getInstance()并不是线程可靠安全的,多次的并发执行时候会使得我们多次实例化此对象。
优点:懒汉模式实现了实例创建的延迟加载
缺点:没有加锁时、线程不安全
6. 单例模式之懒汉模式- 方法粒度加锁
/**
* 单例模式之懒汉模式 朴素版本
* @author li
*
*/
public class CopyOfIdentitlyCradNoLazySimpleLock {
private static CopyOfIdentitlyCradNoLazySimpleLock instance;
private String no;
private CopyOfIdentitlyCradNoLazySimpleLock(){}
synchronized public static CopyOfIdentitlyCradNoLazySimpleLock getInstance(){
if(instance == null){
instance = new CopyOfIdentitlyCradNoLazySimpleLock();
instance.setIdentitlyCradNo("No001");
System.out.println("第一次办理身份证!");
}else{
System.out.println("重复办理身份证!");
}
return instance;
}
private void setIdentitlyCradNo(String no){
this.no = no;
}
public String getIdentitlyCradNoLazySimple(){
return no;
}
}
懒汉模式-方法粒度加锁:为了解决普通懒汉式方法的弊端,在创建方法上使用java关键词synchronized为其加上锁,会解决普通懒汉中我们在并发上带来的不安全性。但是同样也带来了弊端,在创建方法粒度上加上synchronized,会遇到阻塞问题。
7. 单例模式之懒汉模式-双重验证
/**
* 单例模式之懒汉模式 双重验证
* @author li
*
*/
public class IdentitlyCradNoDoubleJudge {
private volatile static IdentitlyCradNoDoubleJudge instance;
private String no;
private IdentitlyCradNoDoubleJudge(){}
public static IdentitlyCradNoDoubleJudge getInstance(){
if(instance == null){
synchronized(IdentitlyCradNoDoubleJudge.class){
if(instance == null){
instance = new IdentitlyCradNoDoubleJudge();
instance.setIdentitlyCradNoDoubleJudge("No001");
System.out.println("第一次办理身份证!");
}
}
}else{
System.out.println("重复办理身份证!");
}
return instance;
}
private void setIdentitlyCradNoDoubleJudge(String no){
this.no = no;
}
public String getIdentitlyCradNoDoubleJudge(){
return no;
}
}
单例模式-双重验证:我们将加锁位置放在更加细粒度上使得我们的程序能够跑的更快一点,为什么使用双重验证?在第一次判空中我们并没有对并发进行控制,也就是说会有n次执行通过了第一次判空,再之后加上synchronized后只允许串行的执行,此时在第一个带着空的状态进入到里面后实例化之后,还有n-1次已经通过了第一次判空来到了synchronized前,再次进入到synchronized中如果不加判空那么将会重新实例化。
8. 单例模式之懒汉模式-JAVA IODH最佳实践
/**
* 单例模式之懒汉模式 静态内部类实现
* @author li
*
*/
public class IdentitlyCradNoStaticClass {
private IdentitlyCradNoStaticClass(){}
private static class CradNoStaticClass{
private static IdentitlyCradNoStaticClass instance
= new IdentitlyCradNoStaticClass();
public final static String no = "No001";
}
public static IdentitlyCradNoStaticClass getInstance(){
System.out.println("第一次创建");
return CradNoStaticClass.instance;
}
public String getIdentitlyCradNoStaticClass(){
return CradNoStaticClass.no;
}
}
懒汉模式-IODH静态内部类:此时需要引入内部类的加载机制,在类中声明的内部类只有在使用之后才会被加载进入内存中,通过在类中声明一个内部类并在其内部声明一个私有静态实例对象,在类的外部的公共创建方法中调用内部类中声明的静态实例对象。