一、定义
单例模式是指在内存中只会创建一次且仅创建一次对象的设计模式。单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例。
二、单例模式的实现
实现分为两种:
a. 饿汉方式:程序启动之后,里面创建单例对象
(不存在线程安全问题,但可能会造成资源的浪费)
b. 懒汉方式:当程序调用单例对象的时候才初始化(使⽤时才加载,可以避免资源不必要的浪费)
单例模式的实现步骤:
(饿汉模式和懒汉模式实现模式一模一样)
1.设置私有的构造函数;
2. 声明⼀个私有的对象属性;
3. 提供⼀个公共的获取实例的⽅法。
2.1饿汉模式
不存在线程安全问题
/**
* DataSource的单例模式
* 饿汉模式
*/
public class DataSourceSingleton {
//1.提供一个私有的构造方法(防止外部直接new对象)
private DataSourceSingleton(){
}
//2.创建一个私有的属性对象
private static DataSourceSingleton dataSource = new DataSourceSingleton();
//3.提供公共的对外的单例对象
public static DataSourceSingleton getInstance(){
return dataSource;
}
public static void main(String[] args) {
System.out.println(DataSourceSingleton.getInstance());
}
}
2.2懒汉模式
2.2.1普通懒汉模式
存在线程安全问题
/**
* 单例模式的懒汉模式 1
*/
public class DataSourceSingleton2 {
//1.创建一个私有的构造方法(防止外部直接new对象)
private DataSourceSingleton2(){
}
//2.创建一个私有的属性
private static DataSourceSingleton2 dataSource;
//3.创建一个对外提供访问的单例对象
public static DataSourceSingleton2 getInstance(){
if (dataSource == null){
//第一次访问
dataSource = new DataSourceSingleton2();
}
return dataSource;
}
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();
}
}
结果发现这两个地址完全不同,所以不再满足单例模式
是非线程安全的代码
问题解决:
加锁synchronized可以解决非线程安全问题
/**
* 单例模式的懒汉模式 1
*/
public class DataSourceSingleton2 {
//1.创建一个私有的构造方法(防止外部直接new对象)
private DataSourceSingleton2(){
}
//2.创建一个私有的属性
private static DataSourceSingleton2 dataSource;
//3.创建一个对外提供访问的单例对象
public synchronized static DataSourceSingleton2 getInstance(){
if (dataSource == null){
//第一次访问
dataSource = new DataSourceSingleton2();
}
return dataSource;
}
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();
}
}
虽然解决了线程安全问题,但耗费了资源,不是最高性能。
2.2.2DCL版懒汉模式
双重校验加锁
/**
* 懒汉模式 -3/4
*/
public class DataSourceSingleton3 {
//1.创建一个私有的构造方法
private DataSourceSingleton3(){
}
//2.创建一个私有属性
private static DataSourceSingleton3 dataSource;
//3.创建一个对外提供访问的单例对象
public static DataSourceSingleton3 getInstance(){
if (dataSource == null){//大致分流
synchronized (DataSourceSingleton3.class){
if (dataSource == null){//精细化的判断
dataSource = new DataSourceSingleton3();
}
}
}
return dataSource;
}
}
上面模式还存在非线程安全问题,指令重排序
使用volatile模式(单例模式最终版本)
new一个对象,在JVM中会经过三步:
1.给dataSource分配内存空间
2. 初始化dataSource对象
3.将dataSource指向分配好的内存空间
指令重排序之后,顺序可能会变为:1-3-2
会导致多个线程创建对象时,有可能线程A创建对象的过程中,执行了第1、3步,线程B 判断对象已经不为空,获取到未初始化的对象,直接返回了一个没有初始化的空的对象。
/**
* 懒汉模式 -3/4
*/
public class DataSourceSingleton3 {
//1.创建一个私有的构造方法
private DataSourceSingleton3(){
}
//2.创建一个私有属性
private static volatile DataSourceSingleton3 dataSource;
//3.创建一个对外提供访问的单例对象
public static DataSourceSingleton3 getInstance(){
if (dataSource == null){//大致分流
synchronized (DataSourceSingleton3.class){
if (dataSource == null){//精细化的判断
dataSource = new DataSourceSingleton3();
}
}
}
return dataSource;
}
}