什么是单例模式
一个类只允许创建一个实例,那个类就是单例类。这个模式就是单例模式。
单例实现模式
饿汉式 | 懒汉式 | 懒汉式(双重检查) | Java 静态内部类 | Java 枚举 | Go sync.Once | |
---|---|---|---|---|---|---|
复杂度 | 简单 | 简单 | 复杂 | 简单 | 简单 | 简单 |
并发安全 | 是 | 是(加锁) | 是(细粒度锁) | 是 | 是 | 是 |
懒加载 | 否 | 是 | 是 | 是 | 是 | 是 |
反射 | 否 | 否 | 否 | 否 | 是 | 否 |
常用 | 是 | 否 | 否 | 是 | 是 | 是 |
饿汉式
Java
public class SingletonOne {
private static final SingletonOne SINGLETON_ONE = new SingletonOne();
private final AtomicInteger id = new AtomicInteger(0);
/**
* 私有化构造函数。可以禁止其他的类直接 new 创建实例。从而保证只有一个实例对象
*/
private SingletonOne() {
}
public static SingletonOne getInstance() {
return SINGLETON_ONE;
}
public Integer getId() {
return id.incrementAndGet();
}
}
go
package singleton
import "sync/atomic"
// singleton 单例结构体。首字母小写为私有模式。不支持外部直接创建实例,从而保证实例唯一。
type singleton struct {
id uint64
}
// singleton 定义单例
var s *singleton
// init 初始化实例
func init() {
s = &singleton{}
}
// GetInstance 公开方法,获取单例对象实例
func GetInstance() *singleton {
return s
}
// GetId
func (s *singleton) GetId() uint64 {
atomic.AddUint64(&s.id, 1)
return s.id
}
懒汉式
java
public class SingletonLock {
private static SingletonLock SINGLETON = null;
private final AtomicInteger id = new AtomicInteger(0);
private SingletonLock() {
}
/**
* synchronized 加锁,保证并发安全
*/
public static synchronized SingletonLock getInstance() {
if (SINGLETON == null) {
SINGLETON = new SingletonLock();
}
return SINGLETON;
}
public Integer getId() {
return id.incrementAndGet();
}
}
go
package singleton
import (
"sync"
"sync/atomic"
)
// singleton_lock 懒汉式(加锁)
type singleton_lock struct {
id uint64
}
var s_lock *singleton_lock
var lock sync.Mutex
func GetLockInstance() *singleton_lock {
// 加锁,粗粒度锁,性能差
// 不加锁,则出现数据竞争时,可能出现多个实例。
// 测试并发不安全时,注释锁代码。
lock.Lock()
defer lock.Unlock()
// 测试并发不安全时,可以打开休眠,人工制造协程上下文切换,从而造成数据竞争。
// time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
if s_lock == nil {
s_lock = &singleton_lock{}
}
return s_lock
}
// GetId
func (s_lock *singleton_lock) GetId() uint64 {
atomic.AddUint64(&s_lock.id, 1)
return s_lock.id
}
懒汉式(双重检查)
Java
package singleton;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Eric
* @date Create 2022/11/28 21:02
*/
public class SingletonTwo {
/**
* new 实例,并非原子操作。volatile 禁止指令重排
*/
private static SingletonTwo SINGLETON_TWO = null;
private final AtomicInteger id = new AtomicInteger(0);
private SingletonTwo() {
}
/**
* 双重检测,将方法上的大锁,优化为细粒度的锁。仅在第一次获取实例时,加锁。初始化实例后则不会再次加锁。
*/
public static SingletonTwo getInstance() {
if (SINGLETON_TWO == null) {
// 细粒度锁.
synchronized (SingletonTwo.class) {
if (SINGLETON_TWO == null) {
// new 新建对象并非原子操作,编译为 class 后为四个指令
// 1. 分配对象内存空间
// 2. 初始化对象
// 3. 设置指针指向堆内存空间
// 4. 初次访问对象
// 操作系统在运行四个指令时会进行指令优化,四个指令可能会指令重排。
// 2,3 指令可以重排序。
// 假设2,3指令发生切换(1,3,2,4)。
// 线程1访问到 指令3 时线程1 被挂起创建
// 切换到线程2,线程2 进入获取实例方法,发现 SINGLETON_TWO 不为 null,直接返回。
// 但实例仅仅创建了内存空间,并未初始化。
SINGLETON_TWO = new SingletonTwo();
}
}
}
return SINGLETON_TWO;
}
public Integer getId() {
return id.incrementAndGet();
}
}
go
package singleton
import (
"sync"
"sync/atomic"
)
type singleton_two struct {
id uint64
}
var instance *singleton_two
var l sync.Mutex
func GetTwoInstance() *singleton_two {
if instance == nil {
l.Lock()
if instance == nil {
instance = &singleton_two{}
}
l.Unlock()
}
return instance
}
func (i *singleton_two) GetId() uint64 {
atomic.AddUint64(&i.id, 1)
return i.id
}
Java 静态内部类
public class SingletonStatic {
private final AtomicInteger id = new AtomicInteger(0);
public SingletonStatic() {
}
/**
* 静态内部类,被调用时候加载。
*/
private static class Inner {
private static final SingletonStatic INSTANCE = new SingletonStatic();
}
public static SingletonStatic getInstance() {
return Inner.INSTANCE;
}
public Integer getId() {
return id.incrementAndGet();
}
}
Java 枚举
public enum SingletonEnum {
/**
* 实例
*/
INSTANCE;
private final AtomicInteger id = new AtomicInteger(0);
public Integer getId() {
return id.incrementAndGet();
}
}
Go one.Once
package singleton
import (
"sync"
"sync/atomic"
)
type singleton_lazy struct {
id uint64
}
var s_lazy *singleton_lazy
var once = &sync.Once{}
func GetLazyInstance() *singleton_lazy {
if s_lazy == nil {
once.Do(func() {
s_lazy = &singleton_lazy{}
})
}
return s_lazy
}
// GetId
func (s_lazy *singleton_lazy) GetLazyId() uint64 {
atomic.AddUint64(&s_lazy.id, 1)
return s_lazy.id
}
优缺点
优点:需要频繁创建的对象或者初始化时间较长的对象使用单例模式可以提高性能。
缺点:扩展性差。