你敢说自己理解单例模式?

作者 : 开心源码 本文共8063个字,预计阅读时间需要21分钟 发布时间: 2022-05-11 共75人阅读

一、背景

最近在学习设计模式,在看到单例模式的时候,我一开始以为直接很理解单例模式了,实现起来也很简单,但是实际上单例模式有着好几个变种,并且多线程中涉及到线程安全问题,那么本文我们就来好好聊聊单例模式,说一下经典三种实现方式:饿汉式、懒汉式、登记式。并且处理掉多线程中可可以出现的线程安全问题。

二、基本概念

1.为什么要用单例模式?

在我们日常的工作中,很多对象通常占使用非常重要的系统资源,比方:IO解决,数据库操作等,那我们必需要限制这些对象只有且始终用一个公使用的实例,即单例。

2.单例模式的实现方式

构造函数私有化,防止其余类生成唯一公使用实例外的实例。且

单例类应该被定义为final,也就是说单例类不可以被继承,由于假如允许继承那子类就都能创立实例,违反了类唯一实例的初衷。

类中一个静态变量来保存单实例的引使用。

一个共有的静态方法来获取单实例的引使用。

3.单例模式的UML类图

4.单例模式的经典实现方式

饿汉式:一开始就创立好实例,每次调使用直接返回,经典的“拿空间换时间”。

懒汉式:推迟加载,第一次调使用的时候才加载,而后返回,以后的每次的调使用就直接返回。经典“拿时间换空间”,多线程环境下要注意处理线程安全的问题。

登记式:对一组单例模式进行的维护,主要是在数量上的扩展,通过线程安全的map把单例存进去,这样在调使用时,先判断该单例能否已经创立,是的话直接返回,不是的话创立一个登记到map中,再返回。

三、饿汉式—代码实现

1.单例类

package com.hafiz.designPattern.singleton;

/**

* Desc: 单例模式-饿汉式

* Created by hafiz.zhang on 2017/9/26.

*/

public class Singleton1 {

? ? // 创立全局静态变量,保证只有一个实例

? ? private static volatile Singleton1 instance? = new Singleton1();

? ? private Singleton1() {

? ? ? ? // 构造函数私有化

? ? ? ? System.out.println(“–调使用饿汉式单例模式的构造函数–“);

? ? }

? ? public static Singleton1 getInstance() {

? ? ? ? System.out.println(“–调使用饿汉式单例模式的静态方法返回实例–“);

? ? ? ? return instance;

? ? }

}

2.测试类

public class DesignPatternTest {

? ? @Test

? ? public void testSingleton1() {

? ? ? ? System.out.println(“—————–测试饿汉式单例模式开始————–“);

? ? ? ? Singleton1 instance1 = Singleton1.getInstance();

? ? ? ? System.out.println(“第二次获取实例”);

? ? ? ? Singleton1 instance2 = Singleton1.getInstance();

? ? ? ? System.out.println(“instance1和instance2能否为同一实例?” + (instance1 == instance2));

? ? ? ? System.out.println(“—————–测试饿汉式单例模式结束————–“);

? ? }

}

3.测试结果

四、懒汉式—代码实现

1.单例类

package com.hafiz.designPattern.singleton;

/**

* Desc:单例模式-懒汉式

* Created by hafiz.zhang on 2017/9/26.

*/

public class Singleton2 {

? ? // 创立全局静态变量,保证只有一个实例

? ? private static Singleton2 instance = null;

? ? // 构造函数私有化

? ? private Singleton2() {

? ? ? ? System.out.println(“–调使用懒汉式单例模式的构造方法–“);

? ? }

? ? public static Singleton2 getInstance() {

? ? ? ? System.out.println(“–调使用懒汉式单例模式获取实例–“);

? ? ? ? if (instance == null) {

? ? ? ? ? ? System.out.println(“–懒汉式单例实例未创立,先创立再返回–“);

? ? ? ? ? ? instance = new Singleton2();

? ? ? ? }

? ? ? ? return instance;

? ? }

}

2.测试类

public class DesignPatternTest {

? ? @Test

? ? public void testSingleton2() {

? ? ? ? System.out.println(“—————–测试懒汉式单例模式开始————–“);

? ? ? ? Singleton2 instance1 = Singleton2.getInstance();

? ? ? ? System.out.println(“第二次获取实例”);

? ? ? ? Singleton2 instance2 = Singleton2.getInstance();

? ? ? ? System.out.println(“instance1和instance2能否为同一实例?” + (instance1 == instance2));

? ? ? ? System.out.println(“—————–测试懒汉式单例模式结束————–“);

? ? }

}

3.测试结果

细心的同学已经发现,这种实现方式,在多线程的环境中,是有线程安全安全问题的,有可可以两个或者多个线程判断instance都为null,而后创立了好几遍实例,不符合单例的思想,我们能对它进行改进。

五、改进懒汉式1—代码实现

原理:用JDK的synchronized同步代码块来处理懒汉式线程安全问题。

1.单例类

package com.hafiz.designPattern.singleton;

/**

* Desc:单例模式-懒汉式

* Created by hafiz.zhang on 2017/9/26.

*/

public class Singleton2 {

? ? // 创立全局静态变量,保证只有一个实例

? ? private static Singleton2 instance = null;

? ? // 构造函数私有化

? ? private Singleton2() {

? ? ? ? System.out.println(“–调使用懒汉式单例模式的构造方法–“);

? ? }

? ? public static Singleton2 getInstance() {

? ? ? ? System.out.println(“–调使用懒汉式单例模式获取实例–“);

     if (instance != null) {

        System.out.println(“–懒汉式单例实例已经创立,直接返回–“);

? ? ? ? ? ?  return instance;

     }

? ? ? ? synchronized (Singleton2.class) {

? ? ? ?    if (instance == null) {

? ? ? ? ? ?   System.out.println(“–懒汉式单例实例未创立,先创立再返回–“);

? ? ? ? ? ?   instance = new Singleton2();

? ? ? ?    }

? ? ? ? }

? ? ? ? return instance;

? ? }

}

2.测试结果

六、改进懒汉式2—代码实现

原理:用JVM隐含的同步和类级内部类来处理,JVM隐含的同步处理了多线程情况下线程安全的问题,类级内部类处理只有用的时候才加载(推迟加载)的问题。

1.JVM隐含的同步有哪些?

静态初始化器(在静态字段上或者static{}静态代码块的初始化器)初始化数据时

访问final字段时

在创立线程之前创立对象时

线程能看见它将要解决的对象时

2.什么是类级内部类?

有static修饰的成员式内部类。没有static修饰的成员式内部类叫对象级内部类。

类级内部类相当于其外部类的static成分,他的对象与外部类对象间不存在依赖关系,因而可直接创立,而对象级内部类的实例,是绑定在外部对象实例中的。

类级内部类中,能定义静态的方法。在静态的方法中只可以够引使用外部类的中的静态成员方法或者者成员变量

类级内部类相当于其外部类的成员,只有在第一次被用的时候才会被装载

3.单例类

package com.hafiz.designPattern.singleton;

/**

* Desc:单例模式-改进懒汉式

* Created by hafiz.zhang on 2017/9/26.

*/

public class Singleton3 {

? ? private static class Singleton4 {

? ? ? ? private static Singleton3 instance;

? ? ? ? static {

? ? ? ? ? ? System.out.println(“–类级内部类被加载–“);

? ? ? ? ? ? instance = new Singleton3();

? ? ? ? }

? ? ? ? private Singleton4() {

? ? ? ? ? ? System.out.println(“–调使用类级内部类的构造函数–“);

? ? ? ? }

? ? }

? ? private Singleton3() {

? ? ? ? System.out.println(“–调使用构造函数–“);

? ? }

? ? public static Singleton3 getInstance() {

? ? ? ? System.out.println(“–开始调使用共有方法返回实例–“);

? ? ? ? Singleton3 instance;

? ? ? ? System.out.println(“—————————“);

? ? ? ? instance = Singleton4.instance;

? ? ? ? System.out.println(“返回单例”);

? ? ? ? return instance;

? ? }

}

4.测试类

package com.hafiz.www;

import com.hafiz.designPattern.observer.ConcreteObserver;

import com.hafiz.designPattern.observer.ConcreteSubject;

import com.hafiz.designPattern.singleton.Singleton1;

import com.hafiz.designPattern.singleton.Singleton2;

import com.hafiz.designPattern.singleton.Singleton3;

import com.hafiz.designPattern.singleton.Singleton4;

import com.hafiz.designPattern.singleton.Singleton4Child1;

import com.hafiz.designPattern.singleton.SingletonChild2;

import org.junit.Test;

/**

* Desc:设计模式demo单元测试类

* Created by hafiz.zhang on 2017/7/27.

*/

public class DesignPatternTest {

? ? @Test

? ? public void testSingleton3() {

? ? ? ? System.out.println(“—————–测试改进懒汉式单例模式开始————–“);

? ? ? ? Singleton3 instance1 = Singleton3.getInstance();

? ? ? ? System.out.println(“第二次获取实例”);

? ? ? ? Singleton3 instance2 = Singleton3.getInstance();

? ? ? ? System.out.println(“instance1和instance2能否为同一实例?” + (instance1 == instance2));

? ? ? ? System.out.println(“—————–测试改进懒汉式单例模式结束————–“);

? ? }

}

5.测试结果

七、登记式–代码实现

1.基类

package com.hafiz.designPattern.singleton;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

/**

* Desc: 单例模式-登记式

* Created by hafiz.zhang on 2017/9/26.

*/

public class Singleton4 {

? ? private static Map map = new ConcurrentHashMap<>();

? ? protected Singleton4() {

? ? ? ? System.out.println(“–私有化构造函数被调使用–“);

? ? }

? ? public static Singleton4 getInstance(String name) {

? ? ? ? if (name == null) {

? ? ? ? ? ? name = Singleton4.class.getName();

? ? ? ? ? ? System.out.println(“–name为空,默认赋值为:–” + Singleton4.class.getName());

? ? ? ? }

? ? ? ? if (map.get(name) != null) {

? ? ? ? ? ? System.out.println(“name对应的值存在,直接返回”);

? ? ? ? ? ? return map.get(name);

? ? ? ? }

? ? ? ? System.out.println(“name对应的值不存在,先创立,再返回”);

? ? ? ? try {

? ? ? ? ? ? Singleton4 result = (Singleton4)Class.forName(name).newInstance();

? ? ? ? ? ? map.put(name, result);

? ? ? ? ? ? return result;

? ? ? ? } catch (InstantiationException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } catch (IllegalAccessException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } catch (ClassNotFoundException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? return null;

? ? }

? ? public Map getMap() {

? ? ? ? return map;

? ? }

}

2.子类1

package com.hafiz.designPattern.singleton;

/**

* Desc:

* Created by hafiz.zhang on 2017/9/26.

*/

public class Singleton4Child1 extends Singleton4 {

? ? public static Singleton4Child1 getInstance() {

? ? ? ? return (Singleton4Child1) Singleton4.getInstance(“com.hafiz.designPattern.singleton.Singleton4Child1”);

? ? }

}

3.子类2

package com.hafiz.designPattern.singleton;

/**

* Desc:

* Created by hafiz.zhang on 2017/9/26.

*/

public class SingletonChild2 extends Singleton4 {

? ? public static SingletonChild2 getInstance() {

? ? ? ? return (SingletonChild2) Singleton4.getInstance(“com.hafiz.designPattern.singleton.SingletonChild2”);

? ? }

}

4.测试类

public class DesignPatternTest {

? ? @Test

? ? public void testSingleton4() {

? ? ? ? System.out.println(“—————–测试登记式单例模式开始————–“);

? ? ? ? System.out.println(“第一次获得实例”);

? ? ? ? Singleton4 instance1 = Singleton4.getInstance(null);

? ? ? ? System.out.println(“res:” + instance1);

? ? ? ? System.out.println(“第二次获取实例”);

? ? ? ? Singleton4Child1 instance2 = Singleton4Child1.getInstance();

? ? ? ? System.out.println(“res:” + instance2);

? ? ? ? System.out.println(“第三次获取实例”);

? ? ? ? SingletonChild2 instance3 = SingletonChild2.getInstance();

? ? ? ? System.out.println(“res:” + instance3);

? ? ? ? System.out.println(“第四次获取实例”);

? ? ? ? SingletonChild2 instance4 = new SingletonChild2();

? ? ? ? System.out.println(“res:” + instance4);

? ? ? ? System.out.println(“输出父类Map中所有的单例”);

? ? ? ? Map map = instance1.getMap();

? ? ? ? for (Map.Entry item : map.entrySet()) {

? ? ? ? ? ? System.out.println(“map-item:” + item.getKey() + “=” + item.getValue());

? ? ? ? }

? ? ? ? System.out.println(“instance1和instance2能否为同一实例?” + (instance1 == instance2));

? ? ? ? System.out.println(“—————–测试登记式单例模式结束————–“);

? ? }

}

5.测试结果

该处理方案的缺点:基类的构造函数对子类公开了(protected),有好的处理方案的博友能探讨指教~

八、总结

经过本文,我们就搞明白了什么叫单例模式,如何优雅的实现经典的单例模式,如何进行拓展和开发具备线程安全的单例模式。对于我们以后的开发非常有帮助,也让我们更加理解单例模式。

{附}:大家能点击加入群:【java高级架构进阶】:https://jq.qq.com/?_wv=1027&k=575y0Kj里面有Java高级大牛直播讲解知识点 走的就是高端路线(假如你想跳槽换工作 但是技术又不够 或者者工作上遇到了瓶颈 我这里有一个JAVA的免费直播课程 讲的是高端的知识点基础不好的误入哟 只需你有1-5年的开发经验能加群找我要课堂链接 注意:是免费的 没有开发经验误入哦)

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 你敢说自己理解单例模式?

发表回复