目录
1.设计模式是啥?
2.单例模式是啥?
3.单例模式的实现步骤
3.1.设置私有的构造函数(为了防止其他类直接new此对象)。
3.2.声明⼀个私有的对象属性。
3.3.提供⼀个公共的获取实例的⽅法。
4.单例模式具体的实现方式:分成 "饿汉" 和 "懒汉" 两种
4.1.饿汉方式
4.2.懒汉方式
a.全局加锁(线程安全、性能比较低)
b.局部加锁【最终版本】(线程安全,双重效验锁DCL(Double Check Lock))
设计模式好⽐象棋中的 "棋谱",红⽅当头炮, ⿊⽅⻢来跳。针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀些固定的套路。按照套路来⾛局势就不会吃亏。
软件开发中也有很多常⻅的 "问题场景",针对这些问题场景, ⼤佬们总结出了⼀些固定的套路,按照这个套路来实现代码, 也不会吃亏。
单例模式是校招中最常考的设计模式之一。
单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例。
这⼀点在很多场景上都需要。⽐如 JDBC 中的 DataSource 实例就只需要⼀个。
程序启动之后,立马创建单例对象。
优点:线程安全的。
缺点:可能造成资源的浪费,通常不使用。
/*** DataSource单例模式* 饿汉模式*/
public class DataSourceSingleton {//1.创建一个私有的构造函数(为防止其他类直接new此对象)private DataSourceSingleton() {}//2.创建一个私有的属性对象private static DataSourceSingleton dataSource = new DataSourceSingleton(); //随着程序(JVM)启动而执行//3.创建一个公共的对外暴露的单例对象public static DataSourceSingleton getInstance() {return dataSource;}
}
public class DataSourceTest {public static void main(String[] args) {System.out.println(DataSourceSingleton.getInstance());}
}
当有程序调用单例对象时才初始化,可以避免资源不必要的浪费。
public class DataSourceSingleton2 {//1.私有的构造方法private DataSourceSingleton2() {}//2.创建一个私有的属性private static DataSourceSingleton2 dataSource;//3.创建一个对外提供访问的单例对象public static DataSourceSingleton2 getInstance() {if(dataSource == null){ //第一次访问try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}dataSource = new DataSourceSingleton2(); //当线程1和线程2同时走到这里,线程1->null->对象1;线程2->null->对象2.不满足单例模式的定义。}return dataSource;}
}
public class DataSourceTest {public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println(DataSourceSingleton2.getInstance());});Thread t2 = new Thread(() -> {System.out.println(DataSourceSingleton2.getInstance());});t1.start();t2.start();}
}
默认的懒汉模式是非线程安全的,使用要对懒汉进行优化,优化改进:
/*** 单例模式--懒汉模式1*/
public class DataSourceSingleton2 {//1.私有的构造方法private DataSourceSingleton2() {}//2.创建一个私有的属性private static DataSourceSingleton2 dataSource;//3.创建一个对外提供访问的单例对象public synchronized static DataSourceSingleton2 getInstance() {if(dataSource == null){ //第一次访问try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}dataSource = new DataSourceSingleton2(); }return dataSource;}
}
/*** 单例模式--懒汉模式2*/
public class DataSourceSingleton3 {//1.私有的构造方法private DataSourceSingleton3() {}//2.私有属性private static volatile DataSourceSingleton3 dataSource = null; //属性标识为volatile//3.公共的方法,得到单例对象public static DataSourceSingleton3 getInstance() {if (dataSource == null) { //大致分流状态 DCLsynchronized (DataSourceSingleton3.class) { //排队执行if(dataSource == null) { //精细化判断(分流) DCLdataSource = new DataSourceSingleton3();}}}return dataSource;}
}
若属性没有标识为volatile,则对象创建需要三步:
memory = allocate() //分配内存
ctorInstanc(memory) //初始化对象
instance = memory //设置instance指向刚分配的地址
假如因为指令重排导致执⾏的顺序变为了132,那么假如a线程中执⾏完13之后,b线程到达代码2处,执⾏判断语句,发现instance指向的是⼀段地址,因此直接不进⼊判断语句,⽽是直接返回了⼀个没有初始化的空的对象。
下一篇:RPC通信相关