目录
2.3 双检锁/双重校验锁(DCL,即 double-checked locking)
一.单例模式的概念
- 单例模式(Singleton Pattern)属于创建型模式;
- 单例模式涉及一个单一的类,该类只负责创建自己的对象,并且保证只有一个对象被创建,该类提供一种访问其唯一的对象的方式;
- 单例模式必须自己创建自己的唯一实例;
二.单例模式介绍
- 解决问题:一个全局使用的类频繁地创建与销毁;
- 使用时机:需要为系统提供一个唯一的实例:有状态的工具类对象(java的calendar);频繁访问数据库或者文件的对象;
- 关键代码:构造函数私有(所以单例类不可被继承);getinstance()方法为static静态方法,要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化;
- 实例:1.多台打印机,但只有一个输出池;2.Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行;
- 三要素:
- 1.私有的构造方法;
- 2.以自己实例为返回值的静态的公有方法。
- 3.指向自己实例的私有静态引用;
三.代码实例
1.饿汉式单例和懒汉式单例
- 饿汉式单例:立即加载,在类加载初始化时就主动创建实例
- 懒汉式单例:延时加载(Lazy Loading),在类加载是只是声明实例,并不创建;在真正使用时通过getInstance方法创建实例。
注:两种经典单例方法都是实例化一个对象并交给自己的引用,只是创建时机不同。
1.1饿汉式单例:
// 饿汉式单例
public class Singleton1 {
// 私有的构造方法
private Singleton1(){}
// 指向自己实例的私有静态引用,主动创建(饿汉式)
private static Singleton1 singleton1 = new Singleton1();
// 以自己实例为返回值的静态公有方法,静态工厂方法
public static Singleton1 getSingleton1() {
return singleton1;
}
}
1.2懒汉式单例:
//懒汉式单例
public class Singleton2 {
// 私有的构造方法
private Singleton2(){}
// 指向自己实例的私有静态引用,不主动创建实例
private static Singleton2 singleton2 = null;
// 以自己实例为返回值的静态公有方法,静态工厂方法
public static Singleton2 getSingleton2() {
// 被动创建,在真正需要时才创建实例
if(singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
访问懒汉式单例方式(其余类型访问方式类似):
Singleton2 singleton2 = Singleton2.getSingleton2();
对比饿汉式和懒汉式,不难发现:在多线程环境下,饿汉式是天生线程安全的,因为饿汉式单例在类被加载时,就应经实例化了一个对象并交给自己的引用 ,因为类在整个生命周期只会加载一次,所以单例类只会创建一个实例,线程每次也只会访问这一个实例,所以饿汉式单例是线程安全的。但是饿汉式没有达到Lazy Loading的效果,会造成资源浪费。
懒汉式在多线程情况下并不可用,因为它不是线程安全的:如果有多个线程想实例singleton,当线程1通过了if(singleton == null),但还未执行singleton = new Singleton(),而此时,线程2进入了if(singleton == null),由于此时singleton的状态还是null,所以线程2也通过了判断,此时两个线程就会实例化两个Singleton对象,产生了线程安全问题。
2.如何使懒汉式单例实现线程安全
2.1 synchronized方法
//同步延迟加载 — synchronized方法
public class Singleton3 {
private static Singleton3 singleton3 = null;
private Singleton3(){}
// synchronized关键字,临界资源的同步互斥访问
public static synchronized Singleton3 getSingleton3() {
if(singleton3 == null) {
singleton3 = new Singleton3();
}
return singleton3;
}
}
2.2 synchronized块
使用synchronized线程安全的使用方式:
//使用synchronized块实现懒汉式单例;
//安全的方式
public class Singleton4 {
private static Singleton4 singleton4 = null;
private Singleton4(){}
public static Singleton4 getSingleton4() {
// synchronized块,临界资源的同步互斥访问
synchronized (Singleton4.class) {
if(singleton4 == null) {
singleton4 = new Singleton4();
}
}
return singleton4;
}
}
使用sunnchronized线程不安全的使用方式:
// 使用synchronized块实现懒汉式单例;
// 不安全的方式
public class Singleton6 {
private static Singleton6 singleton6 = null;
private Singleton6(){}
public static Singleton6 getSingleton6() {
//线程不安全,原因类似Singleton3
if(singleton6 == null) {
synchronized (Singleton6.class)
{
singleton6 = new Singleton6();
}
}
return singleton6;
}
}
2.3 双检锁/双重校验锁(DCL,即 double-checked locking)
// 双检锁/双重校验锁(DCL,即 double-checked locking)
public class Singleton5 {
private volatile static Singleton5 singleton5 = null;
private Singleton5(){}
public static Singleton5 getSingleton5() {
// DCL
if(singleton5 == null) {
synchronized (Singleton5.class) {
//只需要在第一次创建实例时才同步,提高了效率
if(singleton5 == null) {
singleton5 = new Singleton5();
}
}
}
return singleton5;
}
}
2.4 登记式/静态内部类的方法
//登记式/静态内部类的方法
public class Singleton7 {
private Singleton7(){}
//私有内部类,按需加载,实现延迟加载
private static class SingletonHolder {
private static Singleton7 singleton7 = new Singleton7();
}
public static Singleton7 getSingleton7() {
return SingletonHolder.singleton7;
}
}
2.5 ThreadLocal方法
3.枚举的方法实现单例模式(666)
//枚举(666)
public enum Singleton8 {
INSTANCE;
public void whateverMethod() {
}
}
四.利用hashcode验证线程安全
谢谢观看,嘿嘿!
参考博客:
Java设计模式—单例设计模式(Singleton Pattern)完全解析_dmk877的专栏-CSDN博客_单例设计模式java单例模式_czqqqqq的博客-CSDN博客_单例