单例模式(Singleton Pattern)——独一无二的对象

xiaoxiao2021-02-28  66

前言

​ 有一些对象其实我们只需要一个,比如说,线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象,充当但印记、显卡等设备的驱动程序的对象,事实上,这个对象只能有一个实例。如果制造出多个实例,就会大致许多问题的产生,例如:程序的异常行为、资源使用过量,或者不一致的结果。

​ 怎么样样才能保证一个类只有一个实例并且能够全局访问到呢?全局变量?那么你必须在程序一开始就创建好对象。万一这个对象非常耗费资源,二程序在这次的执行过程中又一直没有用到它,不就形成浪费了吗?这时候,单例模式就闪亮登场。

概述

定义

​ 单例模式(singleton pattern),head first设计模式中又名单件模式。确保一个类只有一个实例,并提供一个全局访问点。

注意

1、单例类只能有一个实例。2、单例类必须自己创建自己的唯一实例。3、单例类必须给所有其他的对象提供这一实例。

类图

uniqueInstance变量持有唯一的单件实例getInstance()方法是静态公共方法,可以和全局变量一样访问,但是单例可以通过这个方法延迟实例化单例模式的类也可以是一般的类,具有一般的属性和方法

设计模式的实现

从书上和其它博客了解到,单例模式有6种实现,感觉很精妙。

1. 懒汉式,线程不安全

//不支持多线程。没有加锁 synchronized。---再多线程的环境中不算单例 public class Singleto{ private static Singleton uniqueInstance; private Singleton (){} public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }

2. 懒汉式,线程安全

// 能够在多线程中很好的工作,但是,因为加了synchronized锁,效率很低。 public class Singleton { private static Singleton uniqueInstance; private Singleton (){} public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }

3. 饿汉式,线程安全,但是不是延迟加载

// 相对于方法二,这种方式没有加锁,效率会高一些。但是容易产生垃圾对象(?暂时还不懂?) // 类加载时就初始化,不是延迟加载,其实就是一个全局静态变量,很浪费内存。 // 它基于 classloder 机制避免了多线程的同步问题。(不懂) 后续要了解classloader!!! public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return uniqueInstance; } }

4. 双检锁/双重校验锁(DCL,即double-checked locking),线程安全

// 这种方式采用双检锁机制,安全且在多线程情况下能保持高性能。 public class Singleton(){ private static Singleton uniqueInstance; private Singleton(){} public static Singleton getInstance(){ if(uniqueInstance == null){ sychronized(Singleton.class){ if(uniqueInstance == null){ uniqueInstance = new Singleton(); } } } return uniqueInstance; } } // 针对上述中的 sychronized(Singgle.class) 这里做一个说明: //static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。 // 以下摘自 http://blog.csdn.net/shenshibaoma/article/details/53009505 // 用以记录 // synchronized锁住的是代码还是对象。答案是:synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。 // 当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。 // 所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。原因是基于以上的思想,锁的代码段太长了,别的线程是不是要等很久,等的花儿都谢了。

5. 登记式/静态内部类,线程安全

// 这种方式,达到双检锁方式一样的效果,实现更简单。同样采用classloader机制,来保证初始化 instance 时只有一个线程。但是与第三种方式不同的是,这里只有getInstance被调用后,才会实例化对象。实现了延迟加载。 public class Singleton { private static class SingletonHolder { private static final Singleton UNIQUEINSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.UNIQUEINSTANCE; } }

6. 枚举,线程安全——一脸懵逼的代码

// 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。 // ??不懂。。。 public enum Singleton { UNIQUEINSTANCE; public void whateverMethod() { } }

总结

​ 哇~~~突然感觉java基础太差了,最后的设计模式的实现,好多没懂。虽然,单例模式,是我接触的第一个设计模式,但是,说实话4、5、6三种,以前真没见过。涨姿势了,虽然我还没懂呢。等我懂了,我再好好补充补充。QAQ~

转载请注明原文地址: https://www.6miu.com/read-72118.html

最新回复(0)