Java-单例模式
一、名词解释
单例模式,一种常见的设计模式,在这种模式下面,单例对象的实例必须保证只有一个。
常见的线程池、缓存、日志对象等常被设计成单例。
单例模式通常具有如下特点:
-
私有的构造方法;
-
指向自己实例的私有静态引用;
-
以自己实例为返回值的静态的公有方法。
二、实现方式
单例模式可以根据实例化对象的时机不同,分为 懒汉式 和 饿汉式,其中懒汉式单例只有在真正使用的时候,才会实例化一个对象,并交给自己引用;饿汉式单例则是在单例类被加载的时候就实例化一个对象,并交给自己应用。
三、代码实例
懒汉式单例
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又开始执行,他又去初始化了一个实例,显然,这是不行的。
解决方案其实有很多种,这里主要介绍几种本人认为效率比较高的
一、双重检验
1 package com.example.demo.dataStructure.designPatten.singleTon; 2 3 /** 4 * 双重检验懒汉式 5 * */ 6 public class SingletonDemo3 { 7 private static volatile SingletonDemo3 instance = null; // volatile 关键字作用 8 9 private SingletonDemo3() {} 10 11 public static SingletonDemo3 getInstance() { 12 if(instance == null) { 13 synchronized (SingletonDemo3.class) { 14 if(instance == null) { 15 instance = new SingletonDemo3(); 16 } 17 } 18 } 19 return instance; 20 } 21 22 }
二、静态内部类
1 package com.example.demo.dataStructure.designPatten.singleTon; 2 3 public class SingletonDemo4 { 4 private SingletonDemo4() {} 5 6 private static class CreInstance { 7 private static SingletonDemo4 instance = new SingletonDemo4(); 8 } 9 10 public static SingletonDemo4 getInstance() { 11 return CreInstance.instance; 12 } 13 }
三、枚举
package com.example.demo.dataStructure.designPatten.singleTon; public enum SingletonDemo5 { INSTANCE; }
=================================================================
Easier said than done.