一、方式1(最常用,推荐使用)
单例实现方式一: 饿汉式
类加载到内存后,就实例化一个单例,JVM保证线程安全
简单实用,推荐使用。
唯一缺点: 不管用到与否,类装载时就完成加载。
/**
* @description: 单例实现方式一: 饿汉式 <br>
* 类加载到内存后,就实例化一个单例,JVM保证线程安全 <br>
* 简单实用,推荐使用。<br>
* 唯一缺点: 不管用到与否,类装载时就完成加载。
* @author: flygo
* @time: 2022/5/27 22:17
*/
public class SingletonManager01 {
private static final SingletonManager01 INSTANCE = new SingletonManager01();
private SingletonManager01() {}
public static SingletonManager01 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
SingletonManager01 singletonManager01 = SingletonManager01.getInstance();
SingletonManager01 singletonManager011 = SingletonManager01.getInstance();
System.out.println(singletonManager01 == singletonManager011);
}
}
二、方式2
静态语句块,和方式一一样
/**
* @description: 同第一种方式一样,静态语句块实现
* @author: flygo
* @time: 2022/5/27 22:24
*/
public class SingletonManager02 {
private static final SingletonManager02 INSTANCE;
private SingletonManager02() {}
static {
INSTANCE = new SingletonManager02();
}
public static SingletonManager02 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
SingletonManager02 singletonManager02 = SingletonManager02.getInstance();
SingletonManager02 singletonManager021 = SingletonManager02.getInstance();
System.out.println(singletonManager02 == singletonManager021);
}
}
三、方式3(多线程有问题)
懒汉式,虽然达到了按需初始化的目的,但带来了线程不安全的问题
/**
* @description: lazy loading 懒汉式加载,虽然达到了按需加载的目的,但带来了线程不安全的问题
* @author: flygo
* @time: 2022/7/4 09:32
*/
public class SingletonManager03 {
public static SingletonManager03 INSTANCE;
private SingletonManager03() {}
public static SingletonManager03 getInstance() {
if (INSTANCE == null) {
INSTANCE = new SingletonManager03();
}
return INSTANCE;
}
public static void main(String[] args) {
SingletonManager03 instance1 = SingletonManager03.getInstance();
SingletonManager03 instance2 = SingletonManager03.getInstance();
System.out.println(instance1 == instance2);
}
}
- 验证这种方式的问题,模拟多线程模式,分析这种方式的问题
线程1执行到 instance == null时,这时实例还没实例化,线程2也刚好执行到instance == null,线程1和线程2初始化了两个不同实例对象。
为把问题模拟的更明显,在初始化之前,休眠1毫秒,模拟线程被打断,初始化不同的实例,效果更明显。
/**
* @description: lazy loading 懒汉式加载,虽然达到了按需加载的目的,但带来了线程不安全的问题
* @author: flygo
* @time: 2022/7/4 09:32
*/
public class SingletonManager03 {
public static SingletonManager03 INSTANCE;
private SingletonManager03() {}
public static SingletonManager03 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new SingletonManager03();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(
() -> {
System.out.println(SingletonManager03.getInstance().hashCode());
})
.start();
}
}
}
四、方式4
lazy loading 懒汉式加载,虽然达到了按需加载的目的,但带来了线程不安全的问题
通过synchronized加锁的方式解决,同时效率下降了
/**
* @description: lazy loading 懒汉式加载,虽然达到了按需加载的目的,但带来了线程不安全的问题 <br>
* 通过synchronized加锁的方式解决,同时效率下降了
* @author: flygo
* @time: 2022/7/4 09:32
*/
public class SingletonManager04 {
public static SingletonManager04 INSTANCE;
private SingletonManager04() {}
public static synchronized SingletonManager04 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new SingletonManager04();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(
() -> {
System.out.println(SingletonManager04.getInstance().hashCode());
})
.start();
}
}
}
五、方式5(多线程有问题)
妄图通过减少同步代码块的方式提供效率,然后并不行。相当于没有加锁
/**
* @description: lazy loading 懒汉式加载,虽然达到了按需加载的目的,但带来了线程不安全的问题 <br>
* 通过synchronized加锁的方式解决,同时效率下降了
* @author: flygo
* @time: 2022/7/4 09:32
*/
public class SingletonManager05 {
public static SingletonManager05 INSTANCE;
private SingletonManager05() {}
public static SingletonManager05 getInstance() {
if (INSTANCE == null) {
// 妄图通过减少同步代码块的方式提供效率,然后并不行
synchronized (SingletonManager05.class) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new SingletonManager05();
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(
() -> {
System.out.println(SingletonManager05.getInstance().hashCode());
})
.start();
}
}
}
六、方式6
使用双重检查
/**
* @description: lazy loading 懒汉式加载,虽然达到了按需加载的目的,但带来了线程不安全的问题 <br>
* 通过synchronized加锁的方式解决,同时效率下降了,增加双重检查
* @author: flygo
* @time: 2022/7/4 09:32
*/
public class SingletonManager06 {
// 需要加volatile,指令重排问题
public static volatile SingletonManager06 INSTANCE;
private SingletonManager06() {}
public static SingletonManager06 getInstance() {
if (INSTANCE == null) {
// 双重检查
synchronized (SingletonManager06.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new SingletonManager06();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(
() -> {
System.out.println(SingletonManager06.getInstance().hashCode());
})
.start();
}
}
}
七、方式7(完美的方式之一)
静态内部类的方式
JVM保证单例,加载外部类时,不会加载内部类,这样可以实现懒加载
/**
* @description: 静态内部类的方式 <br>
* JVM保证单例,加载外部类时,不会加载内部类,这样可以实现懒加载
* @author: flygo
* @time: 2022/7/4 09:32
*/
public class SingletonManager07 {
private SingletonManager07() {}
private static final class SingletonManager07Holder {
private static final SingletonManager07 INSTANCE = new SingletonManager07();
}
public static SingletonManager07 getInstance() {
return SingletonManager07Holder.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(
() -> {
System.out.println(SingletonManager07.getInstance().hashCode());
})
.start();
}
}
}
八、方式8(完美中的完美)
不仅可以解决多线程同步,还可以解决反序列化问题
/**
* @description: 不仅可以解决多线程同步,还可以解决反序列化问题 <br>
* @author: flygo
* @time: 2022/7/4 09:32
*/
public enum SingletonManager08 {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(
() -> {
System.out.println(SingletonManager08.INSTANCE.hashCode());
})
.start();
}
}
}