单例模式的定义:
单例模式指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,属于创建型设计模式。
单例模式的应用场景:
- 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少GC。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
- 频繁访问数据库或者文件的对象。
实际运用中,J2EE标准中的ServletContext和ServletContextConfig、Spring框架应用中的ApplicationContext、Spring中创建的默认bean、数据库连接池、
全局线程池等等都是单例模式。
单例模式的几种实现方式:
1,饿汉式
package singleton; /** * @ClassName SingletonDemo1 * @Description 饿汉式 * 类加载到内存后,就实例化一个单例对象,JVM保证线程安全 * 唯一缺点:不管用不用都会进行加载,但是影响不大,实际上是最适用的一种方式 * @Author liuyi * @Date 2020/6/7 12:22 * @Version 1.0 */ public class SingletonDemo1 { //两种方式初始化实例,两种方式的效果是一样的 静态常量方式 private static final SingletonDemo1 instance = new SingletonDemo1(); 静态块方式 private static final SingletonDemo1 instance; static { instance = new SingletonDemo1(); } private SingletonDemo1(){ } SingletonDemo1 getInstance(){ return instance; } void main(String[] args) { SingletonDemo1 instance1 = SingletonDemo1.getInstance(); SingletonDemo1 instance2 = SingletonDemo1.getInstance(); System.out.println(instance1==instance2); 返回的结果为true,说明不管取多少次都是同一个实例 } }
2,懒汉式
* @ClassName SingletonDemo2 * @Description 懒汉式 * 需要使用该对象的时候再去实例化 * 缺点:会产生线程安全问题 * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 SingletonDemo2 { SingletonDemo2 instance; SingletonDemo2(){ } SingletonDemo2 getInstance(){ if(instance==null){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = SingletonDemo2(); } main(String[] args) { 为什么说线程不安全 因为使用了全局的静态变量,会造成多线程访问的时候会产生不唯一的实例 for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } 打印的对象完全乱了,根本不是同一个实例 } }
3,饿汉式(加锁)
* @ClassName SingletonDemo2 * @Description 懒汉式(加锁) * 缺点:效率低 * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 SingletonDemo3 { SingletonDemo3 instance; SingletonDemo3(){ } synchronized SingletonDemo3 getInstance(){ ){ instance = SingletonDemo3(); } 加了锁解决了懒汉式的线程不安全问题,但是这样效率就会明显降低 { System.out.println(getInstance()); }).start(); } } }
4,双重检查
* @ClassName SingletonDemo2 * @Description 双重检查(比较完美的写法) * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 SingletonDemo4 { 必须加volatile关键字,防止指令重排 volatile SingletonDemo4 instance; SingletonDemo4(){ } SingletonDemo4 getInstance(){ 为什么要进行双重检查 比如两个线程同时进入该方法,都拿到instance为空,其中一个拿到锁并new了一个实例, 此时另外一个线程它并不知道你已经new了实例,所以当它拿到锁之后会继续new一个实例 所以如果在锁里面继续判断一次是很有必须要的 synchronized (SingletonDemo4.){ ){ instance = SingletonDemo4(); } } } { System.out.println(getInstance()); }).start(); } } }
5,静态内部类方式
package com.liuyi.entity;
/**
* @ClassName SingletonDemo5
* @Description 静态内部类方式
* JVM保证线程安全
* 加载外部类是不会加载内部类,实现了懒加载
* 最完美的写法
* @Author liuyi
* @Date 2020/6/7 13:52
* @Version 1.0
*/
public class SingletonDemo5 {
private SingletonDemo5(){
//防止恶意通过反射破坏单例
if(SingletonDemo5Inside.instance!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
private static class SingletonDemo5Inside{
private static final SingletonDemo5 instance = new SingletonDemo5();
}
public static SingletonDemo5 getInstance(){
return SingletonDemo5Inside.instance;
}
public static void main(String[] args) {
for (int i = 0; i <100 ; i++) {
new Thread(()->{
System.out.println(SingletonDemo5.getInstance());
}).start();
}
}
}
6,枚举方式
* @Author liuyi * @Description 枚举单例 * 不仅可以解决线程同步,还可以防止反序列化(因为枚举类没有构造方法) * @Date 17:28 2020/6/7 * @Param * @return *enum SingletonDemo6 { instance; test(){ System.out.println("测试测试"); } main(String[] args) { instance.test(); { System.out.println(SingletonDemo6.instance); }).start(); } } }
容器式单例解决大规模生产单例问题:
com.gupaoedu.vip.pattern.singleton.register; import java.util.Map; java.util.concurrent.ConcurrentHashMap; * 大规模生产单例 ContainerSingleton { ContainerSingleton(){} static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>(); synchronized Object getInstance(String className){ Object instance = ; 判断集合中是否有当前对象 if(!ioc.containsKey(className)){ { 如果没有,通过反射创建对象,并放入map集合中 instance = Class.forName(className).newInstance(); ioc.put(className,instance); } (Exception e){ e.printStackTrace(); } instance; }else{ 如果集合中有,则直接取出来返回 ioc.get(className); } } }
ThreadLocal单例(线程内单例):
ThreadLocal不能保证创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,是线程安全的。
com.gupaoedu.vip.pattern.singleton.threadlocal; * ThreadLocal实现线程内单例 ThreadLocalSingleton { final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance = new ThreadLocal<ThreadLocalSingleton>(){ @Override protected ThreadLocalSingleton initialValue() { return ThreadLocalSingleton(); } }; ThreadLocalSingleton(){} ThreadLocalSingleton getInstance(){ threadLocaLInstance.get(); } }
单例模式的优点:
- 单例模式可以保证内存中只有一个实例,减少内存的开销。
- 单例模式可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
单例模式的缺点:
- 单例模式一般没有接口,扩展困难,违背开闭原则。
- 单例模式的代码通常写在一个类中,如果设计不合理,则很容易违背单一职责原则。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。