目录
单例模式是一种设计模式。
啥是设计模式?
设计模式好⽐象棋中的"棋谱".红⽅当头炮,⿊⽅⻢来跳.针对红⽅的⼀些⾛法,⿊⽅应招的时候有⼀ 些固定的套路.按照套路来⾛局势就不会吃亏。软件开发中也有很多常⻅的"问题场景".针对这些问题场景,⼤佬们总结出了⼀些固定的套路.按照这个套路来实现代码,也不会吃亏。
单例模式能够保证在线程中只创建唯一一份实例,不会创建出多份实例。
一.饿汉式
类加载的同时,创建实例。
实现步骤:
1.在类内实例化一个 私有,静态 的该类实例。
2.将构造器私有化。
3.提供一个公共的方法,返回该类的实例化对象。
代码展示:
java
/**
* //单例模式
* 饿汉式
*/
class Instance{
//1.实例化Instance线程,将其设为私有,静态
private static Instance single=new Instance();
//2.私有化构造器
private Instance(){
}
//3.提供公共方法,返回类内创建的实例对象
public static Instance getInstance(){
return single;
}
}
二.懒汉式
类加载的时候不会创建实例,第一次使用的时候才创建实例.
实现步骤:
1.创建私有,静态该类对象single,并设为null.
2.将构造器私有化。
3.提供公共方法,判断该类是否已经被创建,若single=null,则实例化对象,否则,直接返回single实例。
代码展示:
java
class InstanceLazy1{
// 1.创建该类对象,设为私有,静态
// 先不实例化 将其设为null
private static InstanceLazy1 single=null;
//2.将构造器私有化
private InstanceLazy1(){}
//3.提供公共的方法,判断该类是否被实例化过
//若未被实例化,则实例化对象,
//否则直接返回single
public static InstanceLazy1 getInstance(){
if(single==null){
return new InstanceLazy1();
}
return single;
}
}
三、懒汉式优化1
在单线程下,懒汉式是线程安全的,但是若是在多线程下,懒汉模式就可能出现线程安全问题。
在两个线程t1,t2下:
为防止在多线程下,创建出多个对象,要使用synchronized锁,将if和new打包成一个原子的,在判断之前,先上锁,这样就能避免在第一次创建对象的时候,引发线程安全问题。
(这里创建多个对象虽然不会出现异常,无实质性错误,但是若创建的对象占用的内存空间是巨大的,此时若创建多个,就会带来非常大的代价,甚至有可能使内存满,)
但是,加锁之后,每次判断该类是否已被创建,都要先上锁,这势必会降低代码的运行效率,且可能出现 线程安全 只可能发生在第一次要创建对象的时候。
四.懒汉式优化2
为提高效率,在上锁之前,先判断是否是否创建过对象。若未创建,再上锁,再次判断是否实例化了对象。注意两次判断的作用是不同的。
第一次判断是为了提高代码执行效率,第二次是判断是否已创建对象。
五.懒汉式优化3
指令重排序问题也有可能引发线程安全问题.
指令重排序:代码优化的一种方式,调整代码的执行顺序,在原来逻辑不变的前提下,提高代码的执行效率。
可以使用volatile关键字来避免底层自动进行指令重排序.
懒汉式可能发生指令重排序问题是在创建对象的时候:
创建对象,分为三步:
1.申请一段内存空间
2.在这个内存上调用构造方法,创建出这个实例
3.将这个内存地址赋给single引用变量.
正常情况下,是按照1 2 3 来执行的,但有时会按照1 3 2来执行,也能创建出实例对象,这就是指令从排序.
但是在多线程下,若按照1 3 2来执行 就会引发线程安全问题.
这里,在代码重排序后创建出来的single对象,属于未初始化的全"0"值,若该对象中有属性或方法,一旦在后面的代码使用该对象,就可能会报错!!!
在single对象处加上volatile关键字修饰,就能禁止代码重排序,防止这样的对象被创建,解决线程安全问题。
总代码:
java
/**
单例模式
懒汉式
/
class Instance{
//1.实例化Instance线程,将其设为私有,静态
private static Instance single=new Instance();
//2.私有化构造器
private Instance(){
}
//3.提供公共方法,返回类内创建的实例对象
public static Instance getInstance(){
return single;
}
}
/**
* 单例模式
* 饿汉式 优化版
*/
class InstanceLazy{
// 定义该类对象,设为私有,静态
// 先不实例化对象 将其设为null
//优化3:为防止出现 指令重排序问题, 防止出现线程安全问题
// 给single对象 加volatile关键字,底层不再进行指令重排序
private static volatile InstanceLazy single=null;
//将构造器私有化
private InstanceLazy(){}
//提供获取对象的公共方法,在方法内创建对象
public static InstanceLazy getInstance(){
Object locker=new Object();
//判断
// 若还未实例化对象,就实例化对象,
if(single==null){//优化2:当第一次创建后,之后就不用再判断了,也就不用再加锁了,
//为了提高代码的效率,先判断一下,若不为空,则不再加锁,直接返回对象
synchronized(locker){//懒汉式 优化1 :为防止出现线程安全问题
//将判断为空和创建新线程,设置为一个 原子的
if(single==null){
return new InstanceLazy();
}
}
}
//若已创建过对象,就直接返回创建过的对象
return single;
}
}
/**
测试用例
/
public class Thread{
public static void main(String[] args) {
Instance instance1=Instance.getInstance();
Instance instance2=Instance.getInstance();
System.out.println(instance1==instance2);//true 这两个对象是同一个对象实例
InstanceLazy single3=InstanceLazy.getInstance();
InstanceLazy single4=InstanceLazy.getInstance();
System.out.println(single3==single4);//true single1和single2是同一个对象
}
}