一、单例模式
单例模式(Singleton Pattern)是一种常用的设计模式,确保一个类只有一个实例,并提供一个全局访问点。它通常用于节省系统资源或实现共享的资源,比如配置文件、数据库连接等。单例模式可以通过多种方法实现,通常有以下几种主要形式:
1. 饿汉式单例
在这种实现中,单例实例在类加载时就创建好,无需等到使用时再创建。因为实例在类加载时创建,所以线程安全,且不允许有延迟初始化。
java
public class Singleton {//1、创建私有对象private static final Singleton singleton = new Singleton();//2、创建私有构造函数private Singleton(){}//3、提供对外获取对象的公共方法public static Singleton getInstance(){return singleton;}
}
@Test(description = "多线程验证饿汉式-单例", threadPoolSize = 2, invocationCount = 10)public void singletonTest(){Singleton instance = Singleton.getInstance();System.out.println("Hash Code: " + instance.hashCode());}
go
// Singleton 定义一个结构体(类)
type Singleton struct {name string
}
// 声明一个结构体实例(对象)
var singleton *Singleton// 定义init函数构造结构体实例(对象)
func init() {fmt.Println("Singleton initialized")singleton = &Singleton{name: "饿汉式单例"}
}
// GetSingLeton 提供一个外获取Singleton对象的函数
func GetSingLeton() *Singleton {return singleton
}
// TestGetInstance 多线程验证饿汉-单例
func TestGetInstance(t *testing.T) {//声明一个计数器var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func(i int) {defer wg.Done()leton := GetSingLeton()fmt.Printf("inst1: %p\n", leton)}(i)}wg.Wait()fmt.Println("All workers done")
}
2. 懒汉式单例
java 为了确保线程安全,可以在 getInstance
方法中使用 synchronized
关键字。然而,这会导致性能下降,因为每次调用都必须进行同步。
public class SingletonLazy {//1、创建私有对象private static SingletonLazy singletonLazy ;//2、创建私有构造函数private SingletonLazy(){}//3、提供对外获取对象的公共方法public static synchronized SingletonLazy getInstance(){if (singletonLazy == null) {singletonLazy = new SingletonLazy();}return singletonLazy;}
}
@Test(description = "多线程验证懒汉式-单例", threadPoolSize = 2, invocationCount = 10)public void singletonLazyTest(){SingletonLazy instance = SingletonLazy.getInstance();System.out.println("Hash Code: " + instance.hashCode());}
go sync.Once 确保初始化逻辑只执行一次。是线程安全的
// SingletonLazy 定义一个结构体(类)
type SingletonLazy struct {name string
}// 声明对象
var (lazySingleton *SingletonLazyonce sync.Once
)func GetLazySingleton() *SingletonLazy {// sync.Once 确保初始化逻辑只执行一次。once.Do(func() {fmt.Println("只执行一次")lazySingleton = &SingletonLazy{name: "懒汉式-单例"}})return lazySingleton
}
// TestGetLazySingleton 多线程下验证懒汉式-单例
func TestGetLazySingleton(t *testing.T) {// 声明一个计算期var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func(i int) {defer wg.Done()getLazySingleton := GetLazySingleton()fmt.Printf("inst1: %p\n", getLazySingleton)}(i)}wg.Wait()fmt.Println("All workers done")
}
3. 双重检查锁定
java
通过双重检查锁定来实现线程安全的懒汉式单例模式,减少了对性能的影响。
synchronized
synchronized
是 Java 提供的关键字,用于同步代码块。- 它确保同时只能有一个线程执行被同步的代码块,这样可以防止多个线程同时访问这段代码并创建多个实例。
(DoubleCheckedSingleton.class)
DoubleCheckedSingleton.class
表示对该类的类对象进行同步。- 这意味着任何线程在进入这个代码块之前必须获得
DoubleCheckedLockingSingleton
类的锁。 - 通过在类级别上同步,不同的实例之间不会相互干扰。
工作机制
当第一次调用 getInstance()
方法时,代码会进行如下操作:
-
第一次检查:在进入同步代码块之前,检查
instance
是否为null
。如果不是null
,则返回已有实例。此检查是为了在实例已经存在时避免不必要的加锁,提升性能。 -
同步块:如果
instance
为null
,进入同步代码块,并对类进行加锁。此时,只有获取了该锁的线程才能继续执行。 -
第二次检查:在同步块内,重新检查
instance
是否为null
。这是因为在不同线程之间,可能有线程在第一个检查后已经创建了实例。 -
实例化:如果依然为
null
,则创建一个新的实例并赋值给instance
。
总结
通过这种方式,只有在 instance
为 null
的情况下,才会使用同步机制,加锁几乎所有线程,以确保只有一个线程创建实例。这种“双重检查”模式在确保线程安全的同时,最大程度地减少性能开销。这样,你可以在多线程环境中安全地获取单例实例。
private static volatile DoubleCheckSingleton instance;private DoubleCheckSingleton() {// 私有构造函数}public static DoubleCheckSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckSingleton.class) {if (instance == null) {instance = new DoubleCheckSingleton();}}}return instance;}
@Test(description = "多线程验证双重检查式-单例", threadPoolSize = 2, invocationCount = 10)public void doubleCheckSingletonTest(){DoubleCheckSingleton instance = DoubleCheckSingleton.getInstance();System.out.println("Hash Code: " + instance.hashCode());}
go
sync.Once 本质就是双重校验的实现,下面是源码
func (o *Once) Do(f func()) {//判断是否执行过该方法,如果执行过则不执行if atomic.LoadUint32(&o.done) == 1 {return}// Slow-path.o.m.Lock()defer o.m.Unlock()//进行加锁,再做一次判断,如果没有执行,则进行标志已经扫行并调用该方法if o.done == 0 {defer atomic.StoreUint32(&o.done, 1)f()}
}
当然也可以自己实现
/锁对象
var lock sync.Mutex//第一次判断不加锁,第二次加锁保证线程安全,一旦对象建立后,获取对象就不用加锁了。
func GetInstance() *Tool {if instance == nil {lock.Lock()if instance == nil {instance = new(Tool)}lock.Unlock()}return instance
}