学习目标:
掌握单例模式,能扩展关联到其他设计模式
学习内容:
1: 单例模式定义
2: 单例模式常用写法
3: 单例模式优缺点
4: 单例模式综合应用
学习时间:
…
学习产出:
单例模式定义: 就是说某个类在程序运行期间只被实例化一次, 也就是说该类在程序的生命周期中只有一个实例对象。
好处: 减少了类实例对象的重复创建,减少了GC压力,提高了程序的性能;
单例模式代码
方式一: 饿汉式
public class ESingleTon {
private static ESingleTon singleTon = new ESingleTon();
private ESingleTon() {}
public static ESingleTon getSingleTon() {
return singleTon;
}
}
测试饿汉式安全性:
public class SingleTonTest {
static volatile int count = 0;
public static void main(String[] args) {
//构造多线程 创建单例对象场景
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
System.out.println("等待的线程数 : " + ++count);
countDownLatch.await();
//获取单例对象
ESingleTon singleTon = ESingleTon.getSingleTon();
System.out.println(singleTon);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
try {
Thread.sleep(3000);
//唤醒 等待线程
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void eSingleTonTest() {
ESingleTon eSingleTon = ESingleTon.getSingleTon();
System.out.println(eSingleTon);
}
}
执行结果是获取的单例对象都是同一个。
方式二: 懒汉式
public class LazySingleTon {
private static LazySingleTon lazySingleTon = null;
private LazySingleTon() {
}
/**
* 懒汉式
* @return
*/
public static LazySingleTon getInstance() {
if(lazySingleTon == null) {
lazySingleTon = new LazySingleTon();
}
return lazySingleTon;
}
}
这个懒汉式写法不是线程安全的。
线程安全的写法可以添加synchronized(性能存在损耗)。
方式三:懒汉式(线程安全)
public class TwoCsLazySingleTon {
private TwoCsLazySingleTon() {
}
//添加volatile是为了防止实例化对象的时候出现指令重排序
//如果不添加volatile可能出现的后果是 new TwoCsLazySingleTon();
//发生在赋值的指令之后,导致 获取到instance为空,出现空指针的问题
private static volatile TwoCsLazySingleTon instnace = null;
public static TwoCsLazySingleTon getInstnace() {
if(null == instnace) {
synchronized (TwoCsLazySingleTon.class) {
if(null == instnace) {
instnace = new TwoCsLazySingleTon();
}
}
}
return instnace;
}
}
方式四:静态内部类单例
public class InnerSingleTon {
private InnerSingleTon() {}
public static class InnerStaticClass {
private static InnerSingleTon singleTon = new InnerSingleTon();
public static InnerSingleTon getSingleTon() {
return singleTon;
}
}
}
public class InnerSingleTon2 {
private InnerSingleTon2() {}
private static class InnerStaticClass2 {
private static InnerSingleTon2 singleTon = new InnerSingleTon2();
}
public static InnerSingleTon2 getInstance() {
return InnerStaticClass2.singleTon;
}
}
方式五:容器式单例模式
这个学过Spring的就应该很好理解;
我们可以先创建一个容器对象,然后将单例对象存储到容器中并添加一个唯一标识进行获取!详见代码:
方式六: cas写法
public class CasSingleTon {
private static final AtomicReference
INSTANCE = new AtomicReference<>();
private CasSingleTon() {
}
public static final CasSingleTon getCasInstance() {
for (;;) {
CasSingleTon casSingleTon = INSTANCE.get();
if(null != casSingleTon) {
return casSingleTon;
}
INSTANCE.compareAndSet(null, new CasSingleTon());
return INSTANCE.get();
}
}
public static void main(String[] args) {
System.out.println(CasSingleTon.getCasInstance());
System.out.println(CasSingleTon.getCasInstance());
System.out.println(CasSingleTon.getCasInstance());
}
}
总结下单例模式如下:
1: 类的构造方法一定是私有的,并且只有这一个构造器。
2: 仅对外提供一个方法来获取类实例对象。
单例模式综合应用场景
1: 线程池
2: 连接池
3:
参考:
https://bugstack.cn/itstack-demo-design/2020/05/31/重学-Java-设计模式-实战单例模式.html