单例设计模式概念
- 什么是单例设计模式
单例设计模式主要为了解决一个类在内存中只存在一个对象,保证对象的唯一,并设置全局的访问点。
- 单例模式的特点
1)为了保证对象的唯一,就不能允许其他类中随意创建对象,因此需要将构造函数私有化。
2)根据上一步的说法,在其他类中不能创建对象,那就需要在本类中创建对象。
3)其他类中不能创建该类的对象,也就不能调用成员方法和成员属性,该类中需要提供公共静态的获取对象的方法。
- 有哪些常见对象是单例的
比如:
1)ServletContext
2)ServletConfig
3)ApplicationContext
- 常见的单例设计模式
1)饿汉式单例设计模式
2)懒汉式单例设计模式
3)注册式单例设计模式
4)ThreadLocal单例设计模式
一、饿汉式单例设计模式
-
重点:在单例类中首次加载时就创建实例
-
单例类
public class Singleton {
//私有构造方法
private Singleton(){}
//实例化对象,需要静态,全局唯一的实例,可以将其声明为final
private static final Singleton singleton = new Singleton();
//提供外部访问的方法,外部没有对象,需要用类名调用,方法静态
public static Singleton newInstance(){
return singleton;
}
}
- 缺点:因为是类加载的时候就开始实例化对象,因此,就算该对象不会被使用,也会被实例化,导致浪费空间。
二、懒汉式单例设计模式
-
重点:不同于饿汉式,懒汉式单例设计模式只有被调用时才会创建实例。
-
单例类
public class Singleton {
//私有构造方法
private Singleton() {}
//声明对象,volatile是解决重排序的问题
private volatile static Singleton singleton = null;
//提供公共的访问方法
public static Singleton newInstance(){
//因为synchronized相对低效,可以考虑用这个判断减少锁判断
if(singleton == null){
/*如果是多线程环境里面,可能会出现线程安全的问题
比如:A线程进来,还为修改状态,singleton仍然是null
这个时候B线程也能进来。
所以考虑用synchronized锁住
*/
synchronized (Singleton.class) {
//为了保证只有一个实例,不能每次都创建,用null判断
if(singleton == null) {
return singleton;
}
}
}
return singleton;
}
}
三、使用静态内部类编写懒汉式
- 单例类
public class InnerSingleton {
//私有化构造方法
private InnerSingleton() {}
//提供公共获取对象的方法
public static final InnerSingleton newInstance(){
//调用静态内部类中的静态属性
return LazyHolder.innerSingleton;
}
//定义一个内部类,在内部类中创建静态的InnerSingleton对象
private static class LazyHolder{
private static final InnerSingleton innerSingleton = new InnerSingleton();
}
}
- 重点
1)在这种方式作用没有用到synchronized锁
2)利用内部类特征,LazyHolder里面的逻辑需要等待外部方法调用时才执行,简单了说,只有在newInstance被调用了的时候,才会执行LazyHolder里面创建对象的逻辑。
综合上面:可以将其理解为懒汉式单例模式
四、注册式单例设计
- 概念:将每一个实例都缓存到统一的容器中,使用唯一标识符获取实例,可以考虑使用枚举或者容器的方式实现。
1)枚举式单例设计
- 单例类
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton newInstance(){
return INSTANCE;
}
}
- 枚举类单例模式的测试
public class Test {
public static void main(String[] args) {
EnumSingleton e1 = EnumSingleton.newInstance();
EnumSingleton e2 = EnumSingleton.newInstance();
System.out.println(e1 == e2);
}
}
-
结论:通过执行上述的测试代码,容易得出e1和e2是相等的
-
重点
枚举在设计的时候,就确定了枚举不被序列化和反射进行破坏
2)容器式实现单例设计
- 单例类
public class ContainerSingleton {
//私有化构造方法
private ContainerSingleton(){}
//声明一个map的容器
private static Map<String,Object> map = new ConcurrentHashMap<>();
//提供一个公共的方法
public static Object getBean(String className){
//为了保证getBean是安全的
synchronized (map) {
if (!map.containsKey(className)) {
Object obj = null;
try {
//使用反射实现创建对象
obj = Class.forName(className).newInstance();
//将创建的对象添加到容器中
map.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
//将obj对象返回
return obj;
}
}
//如果没有进入到if里面,则说明该对象已经存在,直接从map容器中获取即可
return map.get(className);
}
}
- 结论:在Spring中就是采用容器式的单例设计模式
五、ThreadLocal单例设计
- ThreadLocal在线程内部里面,是线程安全的,在ThreadLocal源码中,定义了ThreadLocalMap容器,在这个Map容器中将一个线程作为Key,将具体的对象作为值,因此ThreadLocal也算是注册式的容器类型单例设计模式。
- 单例类
public class ThreadLocalSingleton {
//私有化构造方法
private ThreadLocalSingleton(){}
//使用ThreadLocal并重写initialValue方法创建对象
private static final ThreadLocal<ThreadLocalSingleton> threadLocal =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
//提供公共的方法
public static ThreadLocalSingleton newInstance(){
return threadLocal.get();
}
}
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦