一, 名词解释
单例模式, 一种常见的设计模式, 在这种模式下面, 单例对象的实例必须保证只有一个.
常见的线程池, 缓存, 日志对象等常被设计成单例.
单例模式通常具有如下特点:
私有的构造方法;
指向自己实例的私有静态引用;
以自己实例为返回值的静态的公有方法.
二, 实现方式
单例模式可以根据实例化对象的时机不同, 分为 懒汉式 和 饿汉式, 其中懒汉式单例只有在真正使用的时候, 才会实例化一个对象, 并交给自己引用; 饿汉式单例则是在单例类被加载的时候就实例化一个对象, 并交给自己应用.
三, 代码实例
懒汉式单例
- package com.example.demo.dataStructure.designPatten.singleTon;
- /**
- * 单例模式 - 懒汉式
- */
- public class SingletonDemo1 {
- private static SingletonDemo1 instance = null;
- private SingletonDemo1() {}
- public static SingletonDemo1 getInstance() {
- // 真正用到的时候在实例化
- if(instance == null) {
- instance = new SingletonDemo1();
- }
- return instance;
- }
- }
饿汉式单例
- package com.example.demo.dataStructure.designPatten.singleTon;
- /**
- * 单例模式 - 饿汉式
- */
- public class SingletonDemo2 {
- private static SingletonDemo2 instance = new SingletonDemo2(); // 类加载的时候实例化
- private SingletonDemo2() {}
- public static SingletonDemo2 getInstance() {
- return instance;
- }
- }
四, 多线程下的单例
上面两种写法, 在单线程的环境中, 自然是没有问题, 都能保证只有一个该类的实例. 但是在多线程的环境中呢, 结果又是怎么样的呢?
我们先来写个多线程的测试类
- package com.example.demo.dataStructure.designPatten.singleTon;
- public class Test {
- public static void main(String[] args) {
- // 起 10 个线程
- for (int i = 0; i < 10; i++) {
- new TestThread().start();;
- }
- }
- }
- class TestThread extends Thread {
- @Override
- public void run() {
- int hash = SingletonDemo1.getInstance().hashCode();// 更换类名来获取不同的实例
- System.out.println(hash);
- }
- }
饿汉式和懒汉式分别测试下, 输出结果如下:
显然, 懒汉式单例在多线程的情况下, 不是真正意义上的单例, 他不是线程安全的, 而饿汉式单例, 由于它在线程访问之前就已经实例化好了 (该类被加载的时候, 实例化, 而且类在其生命周期内只能加载一次, 天生的线程安全), 所以它是线程安全的.
那么, 懒汉式的问题出在哪里呢?
其实, 静下来分析之后, 可以发现, 问题就出在这一句
if(instance == null){...}
试想下这样的情景: 线程 A 和线程 B 都同时走到 if 这个判断, 这是线程 A 突然因故挂起, 线程 B 继续执行, 初始化一个实例, 这
时线程 A 又开始执行, 他又去初始化了一个实例, 显然, 这是不行的.
解决方案其实有很多种, 这里主要介绍几种本人认为效率比较高的
一, 双重检验
- package com.example.demo.dataStructure.designPatten.singleTon;
- /**
- * 双重检验懒汉式
- * */
- public class SingletonDemo3 {
- private static volatile SingletonDemo3 instance = null; // volatile 关键字作用
- private SingletonDemo3() {}
- public static SingletonDemo3 getInstance() {
- if(instance == null) {
- synchronized (SingletonDemo3.class) {
- if(instance == null) {
- instance = new SingletonDemo3();
- }
- }
- }
- return instance;
- }
- }
二, 静态内部类
- package com.example.demo.dataStructure.designPatten.singleTon;
- public class SingletonDemo4 {
- private SingletonDemo4() {}
- private static class CreInstance {
- private static SingletonDemo4 instance = new SingletonDemo4();
- }
- public static SingletonDemo4 getInstance() {
- return CreInstance.instance;
- }
- }
三, 枚举
- package com.example.demo.dataStructure.designPatten.singleTon;
- public enum SingletonDemo5 {
- INSTANCE;
- }
这个其实可以看下这个类的. class 文件就明白了.
来源: http://www.bubuko.com/infodetail-2765733.html