NOTE
6.2 单例模式
一个类有且仅有一个实例对象 1. uml 2. Java 2.1. 饿汉式 - MySingleton - Client - 特点 - 会在加载类后一开始就被初始化,即使这个实例没有被使用到 2.2. 懒汉式:对整个方法加锁 - 特点 - 只有在真正用到的时候才会创建实例 - 每次调用都要加锁,但其
这是历史学习笔记,可能存在过时或不完整的理解。
一个类有且仅有一个实例对象
1. uml
2. Java
2.1. 饿汉式
- MySingleton
public class MySingleton
{
//jvm加载到该文件时马上创建对象
private static MySingleton singleton = new MySingleton();
//外部不能创建
private MySingleton()
{}
//只能通过这个方法获取实例
public static MySingleton getSingleton()
{
return singleton;
}
}
- Client
public class Client
{
public static void main(String[] args)
{
MySingleton singleton = MySingleton.getSingleton();
MySingleton singleton2 = MySingleton.getSingleton();
//同一个对象实例
System.out.println(singleton == singleton2);
}
}
- 特点
- 会在加载类后一开始就被初始化,即使这个实例没有被使用到
2.2. 懒汉式:对整个方法加锁
public class MySingleton
{
private static MySingleton singleton = null;
//外部不能创建
private MySingleton()
{}
public static synchronized MySingleton getInstance() {//必须用synchronized修饰保证线程安全
if (instance == null) {
instance = new MySingleton();
}
return instance;
}
}
- 特点
- 只有在真正用到的时候才会创建实例
- 每次调用都要加锁,但其实只有第一次创建的时候才需要
2.3. 懒汉式:双重检测锁/DCL
2.3.1. 第一个版本:只用sychronized块
public class MySingleton
{
private static MySingleton singleton = null;
//外部不能创建
private MySingleton()
{}
public static MySingleton getSingleton() {
if (instance == null) { //Single Checked,可能有多个线程同时进入到这里
synchronized (MySingleton.class) {
instance = new MySingleton();
}
}
return instance ;
}
}
2.3.2. 第二个版本:sychronized块+double check
public class MySingleton
{
private static MySingleton singleton = null;
//外部不能创建
private MySingleton()
{}
public static MySingleton getSingleton() {
if (instance == null) { //Single Checked
synchronized (MySingleton.class) {
if (instance == null) { //Double Checked
instance = new MySingleton();
}
}
}
return instance ;
}
}
- 问题:指令重排序
对象的创建过程.md
instance = new MySingleton()有问题,这句话分成三步- 分配内存
- 调用 Singleton 的构造函数来初始化成员变量
- 将instance变量的引用指向分配的内存空间 JIT存在指令重排序,如果线程1的顺序是1-3-2,线程2的顺序是1-2-3。 当线程1执行到3的时候,线程2执行获取了单例,但是还没有初始化,那么使用时就会出错
- 解决 加上volatile,利用其有序性
2.3.3. 第三个版本:sychronized块+double check+volatile
public class MySingleton {
private volatile static MySingleton instance; //声明成 volatile
private MySingleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
根据volatile的happen before规则,volatile写happen before于volatile 读之前。
2.4. 静态内部类
public final class MySingleton
{
private static class SingletonHolder
{
private static final MySingleton INSTANCE = new MySingleton();
}
private MySingleton()
{
}
public static MySingleton getInstance()
{
return SingletonHolder.INSTANCE;
}
}
- 特点
- 由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的
- 加载MySingleton类的时候如果没有用到内部类,那么内部类不会加载,如test方法;只有在用到静态内部类的时候才会加载,如getInstance方法
2.5. Enum
public enum MySingleton
{
SINGLETON(10);
private Integer val;
MySingleton(Integer val)
{
this.val = val;
}
public static void main(String[] args)
{
System.out.println(MySingleton.SINGLETON == MySingleton.SINGLETON);//true
}
}
3. Golang
3.1. 线程不安全
- singleton
type singleton struct {
}
//private
var instance *singleton
//public
//线程不安全
func GetInstance() *singleton {
if instance == nil {
instance = &singleton{}
}
return instance
}
- client
func main() {
instance := 单例.GetInstance()
instance2 := 单例.GetInstance()
fmt.Println(instance == instance2)
}
3.2. 线程安全
//线程安全 加锁
var mutex sync.Mutex
func GetInstance2() *singleton {
mutex.Lock()
defer mutex.Unlock()
if instance == nil {
instance = &singleton{}
}
return instance
}
3.3. 线程安全DCL
//DCL
func GetInstance3() *singleton {
if instance == nil {
mutex.Lock()
defer mutex.Unlock()
if instance == nil {
instance = &singleton{}
}
}
return instance
}
3.4. sync.Once
//只执行一次
var once sync.Once
func GetInstance4() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}