单例设计模式(Go、Java)

 

单例设计模式 golang、java实现

一、单例模式是什么?

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式的要点有三个:

  1. 单例类只能有一个实例
  2. 单例类必须自己创建自己的唯一实例
  3. 单例类必须给所有其他对象提供这一实例

二、类图

单例设计模式(Go、Java)_第1张图片

 

三、模式

懒汉模式:

懒汉式一开始不会实例化,什么时候用就什么时候new,才进行实例化。

懒汉式一般使用都会加同步锁,效率比饿汉式差。

饿汉模式:

饿汉式在一开始类加载的时候就已经实例化,并且创建单例对象,以后只管用即可。

饿汉式没有加任何的锁,因此执行效率比较高。

 

四、实现

懒汉式,线程不安全

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作

java:

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

golang:

type singleton struct{} 
var ins *singleton
func GetIns() *singleton{ 
    if ins == nil { 
       ins = &singleton{} 
    } 
    return ins  
}

懒汉式,线程安全

这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

java:

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

golang: 

 type singleton struct{}  
 var ins *singleton  
 var mu sync.Mutex  
 func GetIns() *singleton{  
    mu.Lock()  
    defer mu.Unlock()   
    if ins == nil {  
        ins = &singleton{} 
     } 
    return ins 
}

饿汉式

这种方式比较常用,但容易产生垃圾对象。

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

java:

public class Singleton {  
    private static Singleton instance = new Singleton(); 
     
    private Singleton (){
    }  
    
    public static Singleton getInstance() {  
        return instance;  
    }  
}

golang: 

type singleton struct{} 
var ins *singleton = &singleton{} 
func GetIns() *singleton{
    return ins
}

双重校验锁(DCL, double-checked locking)

这种方式采用双锁机制,安全且在多线程情况下能保持高性能 lazy loading

getInstance() 的性能对应用程序很关键。

java:

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    } 
}

golang:

type singleton struct{}  
var ins *singleton  
var mu sync.Mutex  
func GetIns() *singleton{    
    if ins == nil { 
        mu.Lock() 
        defer mu.Unlock()  
        if ins == nil {  
            ins = &singleton{} 
        } 
    } 
    return ins 
}

 Golang once

这种方式只限在Golang中。

1.使用atomic 原子操作计数器,用于记录此Once对象下的done的值,func()方法只执行一次

2.使用Mutex互斥锁保证多协程操作的安全性

不管成功还是失败只执行一次,慎用

golang:

type singleton struct{}
var ins *singleton
var once sync.Once
func GetIns() *singleton {
    once.Do(func(){
        ins = &singleton{}
    })
    return ins
}

五、优缺点

优点:

  • 确保所有的对象都访问一个实例 (一些公共的资源)
  • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
  • 避免对资源的多重占用(比如写文件操作)

缺点:

  • 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态
  • 扩展有很大的困难
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。

六、使用场景

  • 要求生产唯一序列号。
  • WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
  • 加载配置文件

 

你可能感兴趣的:(设计模式,golang,单例模式)