一、单例模式

单例模式(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.WaitGroup
	for 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 *SingletonLazy
	once          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.WaitGroup
	for 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() 方法时,代码会进行如下操作:

  1. 第一次检查:在进入同步代码块之前,检查 instance 是否为 null。如果不是 null,则返回已有实例。此检查是为了在实例已经存在时避免不必要的加锁,提升性能。

  2. 同步块:如果 instancenull,进入同步代码块,并对类进行加锁。此时,只有获取了该锁的线程才能继续执行。

  3. 第二次检查:在同步块内,重新检查 instance 是否为 null。这是因为在不同线程之间,可能有线程在第一个检查后已经创建了实例。

  4. 实例化:如果依然为 null,则创建一个新的实例并赋值给 instance

总结

通过这种方式,只有在 instancenull 的情况下,才会使用同步机制,加锁几乎所有线程,以确保只有一个线程创建实例。这种“双重检查”模式在确保线程安全的同时,最大程度地减少性能开销。这样,你可以在多线程环境中安全地获取单例实例。

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
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值