人物:大鸟,小菜
事件:小菜在点击一个小工具的图标时,每次点击都会创建一个窗体,但是小菜希望的是,第一次点击新建一个窗体,但是后面的每次点击都只会弹出以前创建的那个窗体,小菜很郁闷,于是大鸟让小菜借此学习单例模式。
单例模式:
1.简介单例模式,并讲述了单例模式的懒汉式
2.介绍了单例模式的饿汉式
单例模式(懒汉式)
1.概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.特点:单例模式只能有一个实例;单例类必须创建自己的唯一实例;单例类可以为其他类提供这一实例
3.懒汉式基础代码实现:
Singleton类,即单例类:
public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
客户端:
@Slf4j public class SingletonClient { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); if (s1 == s2) { log.info("两个对象是相同的实例"); } } }
输出结果:
两个对象是相同的实例
4.多线程下怎么保证单例模式线程安全,即加锁
注意:在多线程中,可能多个线程同时访问Singleton类,为了避免创建多个实例,加锁如下:
public class Singleton { private static Singleton instance; private static final Object lock = new Object(); private Singleton() { } public static Singleton getInstance() { synchronized (lock) { if (instance == null) { instance = new Singleton(); } } return instance; } }
小菜:那这样不是会每次都加锁么,可能没有创建实例也直接加锁了?
大鸟:那还可以做如下改动,先判断实例是否为空,再看锁不锁
5.实现双重锁定如下:
public class Singleton { private static Singleton instance; private static final Object lock = new Object(); private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (lock) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
6.静态内部类,保证线程安全由提高点性能:
public class Singleton { private Singleton() { } private static class LazyHolder { private static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return LazyHolder.instance; } }
问:为什么静态内部类是线程安全的?
答:利用了类中静态变量的唯一性,而且不加锁性能会好很多
单例模式(饿汉式)
public class Singleton { private Singleton() { } private static final Singleton single = new Singleton(); public static Singleton getInstance() { return single; } }
问:为什么这种方式是线程安全的?
答:在类创建的时候,也会创建类里面的静态对象,而静态对象一旦创建,又利用了静态变量的唯一性,和懒汉式的静态内部类有些相似
问:那静态内部类和饿汉式有什么区别呢?
答:静态内部类,需要时才加载类中的方法,又因为饿汉式里一开始就将静态变量加载,所以使用上的性能可能没有饿汉式的好
饿汉式,第一次加载类时直接就将静态变量加载了,但是如果后面一直不使用,就会造成内存的浪费