JAVA多线程中的单例模式
一.什么是单例模式?
在 Java 里,单例模式指的是一个类仅有一个实例,并且提供一个全局访问点(公有get方法)来获取该实例。在多线程环境下实现单例模式时,需要保证线程安全,避免多个线程同时创建多个实例。下面介绍几种常见的多线程单例模式实现方式
二.饿汉式
在类加载时就创建了实例
1.代码示例
class Singleton {
//在类加载时直接创建实例
//static 修饰引用变量,意味着这个变量属于类,而不是实例
//确保类只有一个实例。这里用 static 让 instance 属于类,只有一份。
private static Singleton instance = new Singleton();
//提供公有的get方法来让外部获取唯一实例
public static Singleton getInstance() {
return instance;
}
// 把构造方法设为 private。此时在类的外面,就无法继续 new 实例了.
private Singleton() {
}
}
class Demo20 {
public static void main(String[] args) {
// 如果代码这么写,就违背初衷,就应该禁止这个类在类外部被 new.
// Singleton instance2 = new Singleton();
Singleton instance = Singleton.getInstance();
}
}
2.以上代码是线程安全的——为什么?
public static Singleton getInstance() { return instance; }
因为上述代码中这段操作只涉及到“读取数据”,不涉及到修改
三.懒汉式
不是在类加载时就创建了实例
先来看一段代码
1.代码示例
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
// 把构造方法设为 private。此时在类外部,就无法继续 new 实例了.
private SingletonLazy() {}
}
class Demo2 {
public static void main(String[] args) {
SingletonLazy instance = SingletonLazy.getInstance();
SingletonLazy instance2 = SingletonLazy.getInstance();
}
}
2.解释
这种方式在首次调用getInstance( )的时候才会创建实例,后续再调用getInstance( ),则直接返回实例(不new)。
3.但在多线程环境下不安全--为什么?
if (instance == null) { instance = new SingletonLazy(); } return instance;
在上述代码中这段操作既涉及到了读取,也涉及到了修改(将SingletonLazy对象赋值给引用变量instance)
假设有两个线程 t1,t2 进行下方操作
SingletonLazy instance = SingletonLazy.getInstance();
则
导致实例被创建出多份
4.如何修改?
加锁
4.1 synchronized修饰此段代码块
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
//这里
synchronized (Singleton.class){
if(instance == null) {
instance = new SingletonLazy();
}
}
return instance;
}
private SingletonLazy() {}
}
4.2 synchronized修饰getInstance( )方法
class SingletonLazy {
private static SingletonLazy instance = null;
//这里
public static synchronized SingletonLazy getInstance() {
if(instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {}
}
5.有个小的新问题
线程不安全并不是一直都不安全。只是在多线程(多个线程)环境下,首轮调用getInstance( )方法才会不安全。一旦实例创建好,多个线程同时调用getInstance( )方法也并不会触发线程不安全问题。
总:会带来一定的性能开销
6.如何解决?
怎样既保持线程安全,也减少同步带来的性能开销
6.1 解决方法
双重检查锁定
public static SingletonLazy getInstance() { //加上判定条件 if (instance==null){ //在此处加锁 synchronized (Singleton.class){ if(instance == null) { instance = new SingletonLazy(); } } } return instance; }
6.2 新问题 (这里可以不看)
6.2.1 解决方法(这里看)
使用 volatile 关键字保证可见性
private static volatile SingletonLazy instance = null;