单例模式有哪些?懒汉模式和饿汉模式怎么实现。
单例模式有哪些?懒汉模式和饿汉模式怎么实现。
题眼:并发安全
多数情况下会要求手写。
单例模式是一种设计模式,保证一个类只有一个实例,并提供全局访问点。
在单例模式中,常见的实现方式包括:
懒汉模式(Lazy Initialization):在首次使用时才创建实例。在懒汉模式中,通过一个静态变量instance保存实例,在getInstance()方法中进行实例的延迟创建。使用synchronized关键字保证线程安全,但会影响性能。
饿汉模式(Eager Initialization):在类加载时就创建实例。在饿汉模式中,实例在类加载时就被创建,因此不存在线程安全的问题。但如果实例初始化较为复杂,可能会导致类加载较慢。
参考答案:
单例模式可以保证一个类或者一个模块全局唯一,只要初始化以后,无论在程序的哪里访问,都会使用同一个模块,这样可以有效节约内存,比较常见的就是加载配置文件,或者初始化连接池。
单例模式可以分为懒汉式和饿汉式。饿汉式比较简单,它在程序启动的时候就初始化,这样不存在并发安全问题。缺点也很明显,它不管你后面有没有用到这个模块,都会初始化,而且如果这个模块初始化比较耗时,也会拖累程序启动的速度。懒汉式相对饿汉式会复杂一些,它不是程序启动就初始化的,而是在第一次使用这个模块时才初始化,好处是不会增加耗时和资源浪费,缺点是存在并发安全问题。比如说,两个请求同时初始化一个资源,就会出现多次初始化的情况,这样子就失去了单例的作用了。解决这个问题常用的做法是:加锁、双重校验。go的话,还可以用Sync.Once来实现并发安全。
源码:
var GlobalMem *Mem //一个全局变量
var mux Mutex
type Mem struct {
name string
Age string //不要这样定义字段,调用方能直接修改这个字段,不安全。
}
func (m *Mem) GetName() string {
return m.name
}
func (m *Mem) SetName() {
//最好不要提供这种方法,不要把单例的控制权交给任何调用方。
}
func NewGlobalMem() *Mem {
//单例的核心方法
if GlobalMem != nil {
return GlobalMem
}
mux.lock()
GlobalMem = &Mem{name: "关注香香编程喵喵喵,关注香香编程谢谢喵喵喵!"}
mux.unlock()
return GlobalMem
}
func testMem() {
_ = NewGlobalMem()
name := GlobalMem.GetName()
name = NewGlobalMem().GetName()
fmt.Printf(name)
}
var GlobalMem *Mem //一个全局变量
type Mem struct {
name string
Age string //不要这样定义字段,调用方能直接修改这个字段,不安全。
}
func (m *Mem) GetName() string {
return m.name
}
func (m *Mem) SetName() {
//最好不要提供这种方法,不要把单例的控制权交给任何调用方。
}
var once = &sync.Once{}
func NewGlobalMemOnce() *Mem {
once.Do(func() {
//sync.Once 可以保证无论多少并发和调用,这个实例化只会被执行一次。
GlobalMem = &Mem{name: "关注香香编程喵喵喵,关注香香编程谢谢喵喵喵!"}
})
return GlobalMem
}
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
