- 一、定义
- 二、示例:
- 三、7种单例模式的实现
- 0. 静态类使⽤
- 1. 懒汉模式(线程不安全)
- 2.饿汉式(线程安全)
- 3.懒汉式(线程安全)
- 4.使用类的内部类(线程安全)
- 5.双重校验锁(线程安全)
- 6.CAS(线程安全)
- 7.枚举单例(线程安全)
单例模式是java中最简单的设计模式之一,属于创建型模式,提供了创建一个全局实例对象的最佳方式
二、示例:模拟场景:
- 数据库的连接池不会反复创建
- spring中⼀个单例模式bean的⽣成和使⽤
- 在我们平常的代码中需要设置全局的的⼀些属性保存
在不需要维持任何状态下,仅仅⽤于全局访问,这个使⽤使⽤静态类的⽅式更加⽅便
package com.qf.design.create.singleton;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SingleTon00 {
public static Map map=new ConcurrentHashMap<>();
}
1. 懒汉模式(线程不安全)
高并发的场景下就会造成多个同样的实例并存,从⽽没有达到单例的要求
package com.qf.design.create.singleton;
public class SingleTon01 {
private static SingleTon01 instance;
private SingleTon01(){}
public static SingleTon01 getInstance(){
if (instance!=null) return instance;
else instance= new SingleTon01();
return instance;
}
}
2.饿汉式(线程安全)
饿汉式是线程安全的,但是它在一开始就被加载,如果是大的对象情况下,可能开始就占用了很大的内存空间
package com.qf.design.create.singleton;
public class SingleTon02 {
private static SingleTon02 instance=new SingleTon02();
private SingleTon02(){}
public static SingleTon02 getInstance(){
return instance;
}
}
3.懒汉式(线程安全)
此种模式虽然是安全的,但由于把锁加到⽅法上后,所有的访问都因需要锁占⽤导致资源的浪费
package com.qf.design.create.singleton;
public class SingleTon03 {
private static SingleTon03 instance;
private SingleTon03(){}
public synchronized static SingleTon03 getInstance(){
if (instance!=null) return instance;
else instance=new SingleTon03();
return instance;
}
}
4.使用类的内部类(线程安全)
使用类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的⽅式耗费性能。
package com.qf.design.create.singleton;
public class SingleTon04 {
private static class SingleHolder{
private static SingleTon04 instance=new SingleTon04();
}
private SingleTon04(){}
public static SingleTon04 getInstance(){
return SingleHolder.instance;
}
}
5.双重校验锁(线程安全)
双重校验减少了实例的创建消耗,减小了锁的力度
package com.qf.design.create.singleton;
public class SingleTon05 {
private static volatile SingleTon05 instance;
private SingleTon05(){}
public static SingleTon05 getInstance(){
if (instance!=null) return instance;
else synchronized (SingleTon05.class){
if (instance==null){
instance=new SingleTon05();
}
return instance;
}
}
}
6.CAS(线程安全)
java并发库提供了很多原⼦类来⽀持并发访问的数据安全性; AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference 。AtomicReference 可以封装引⽤⼀个V实例,⽀持并发访问如上的单例⽅式就是使⽤了这样的⼀个特点。
使⽤CAS的好处就是不需要使⽤传统的加锁⽅式保证线程安全,⽽是依赖于CAS的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外的开销,并且可以⽀持较⼤的并发性。
package com.qf.design.create.singleton;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
public class SingleTon06 {
private static final AtomicReference INSTANCE = new
AtomicReference();
private static SingleTon06 instance;
private SingleTon06() { }
public static final SingleTon06 getInstance() {
for (; ; ) {
SingleTon06 instance = INSTANCE.get();
if (null != instance) return instance;
INSTANCE.compareAndSet(null, new SingleTon06());
return INSTANCE.get();
}
}
}
7.枚举单例(线程安全)
使⽤枚举的⽅式解决单例模式,此种⽅式可能是平时最少⽤到的,解决线程安全、⾃由串⾏化、单⼀实例
package com.qf.design.create.singleton;
public enum SingleTon07 {
INSTANCE;
public void test(){
System.out.println("hello~~");
}
}
总结:
虽然只是⼀个很平常的单例模式,但在各种的实现上真的可以看到java的基本功的体现,这⾥包括了懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串⾏化等等
在平时的开发中如果可以确保此类是全局可⽤不需要做懒加载,那么直接创建并给外部调⽤即可。
但如果是很多的类,有些需要在⽤户触发⼀定的条件后(游戏关卡)才显示,那么⼀定要⽤懒加载。
线程的安全上可以按需选择



