单例模式概述:
所谓的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法),单例模式每次返回的实例对象的hashCode相同。
一、为什么要使用单例设计模式?
当多个线程要操作同一对象,要保证对象的唯一性,以解决实例化过程中只实例化一次。另外在实际应用到的框架中,比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory,这就会使用到单例设计模式。对于需要频繁创建销毁的对象,使用单例模式可以节省内存资源提高性能。
二、解决思路
1、有一个实例化的过程(只有一次),产生实例化对象。
2、提供返回实例对象的方法
三、单例模式的分类
单例模式有八种方式及优缺点:
1. 饿汉式(静态常量)
1)线程安全性:在加载的时候已经被实例化(只有这一次),所以是线程安全的。
2)性能:没有延迟加载,若长时间不使用或者没有使用过该实例,实例对象会占用资源,造成内存资源浪费,影响性能。
注:JDK中的Runtime就是饿汉式实现
2. 饿汉式(静态代码块)
优缺点与饿汉式(静态常量)一样
3. 懒汉式(线程不安全)
1)线程安全性:只能在单线程下使用,多线程并发情况下不能保证实例对象的唯一性。
2)性能:实现了懒加载,性能相对饿汉式比较好。
注:在多线程情况下,一个线程进入了if (null == instance)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例,所以在多线程环境下不可使用这种方式。
4. 懒汉式(线程安全,同步方法)
1)通过增加synchronized保证了线程安全性。
2)使用synchronized使得程序退化到了串行执行,降低了性能。
注:实际开发不推荐使用
5. 懒汉式(线程安全,同步代码块)
1)不能保证线程安全性
注:实际开发不推荐使用
6. 双重检查
1)通过synchronized以及DCL保证了线程安全性。
2)使用懒加载以及DCL,性能比较好。
注:实际开发推荐使用
7. 静态内部类
1)通过静态内部类实现懒加载,保证线程安全性,是目前应用比较广泛的一种单例模式。
注:内部类的特性是,当外部类被装载的时候,静态内部类是不会被装载的,以此实现懒加载。
8. 枚举
1)同Holder(持有者模式)一样,是目前运用比较广泛的单例模式,大师结晶
四、八种单例模式的代码实现示例
1)饿汉式(可以深入了解JVM ClassLoader的加载机制)
/**
* 饿汉模式(静态变量)
* 1.线程安全性:在加载的时候已经被实例化(只有这一次),所以是线程安全的。
* 2.性能:没有延迟加载,若长时间不使用,实例对象会占用资源,影响性能。
*/
public class HungerySingleton {
// 在加载的时候就产生实例对象(由ClassLoader加载)
private static HungerySingleton instance = new HungerySingleton();
// 实例的属性会占用资源空间
private byte[] data = new byte[1024];
// 单例模式构造器必须私有化,外部不能new
private HungerySingleton {
}
// 返回实例对象
public static HungerySingleton getInstance() {
return instance;
}
// 测试20个线程会返回同一个实例对象
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HungerySingleton.getInstance());
}).start();
}
}
}
2)饿汉式(静态代码块)
/**
* 饿汉模式(静态代码块)
* 1.线程安全性:在加载的时候已经被实例化(只有这一次),所以是线程安全的。
* 2.性能:没有延迟加载,若长时间不使用,实例对象会占用资源,影响性能。
*/
public class HungerySingleton {
private static HungerySingleton instance;
// 实例的属性会占用资源空间
private byte[] data = new byte[1024];
// 单例模式构造器必须私有化,外部不能new
private HungerySingleton {
}
// 在静态代码块中创建单例对象
static {
instance = new HungerySingleton();
}
// 返回实例对象
public static HungerySingleton getInstance() {
return instance;
}
// 测试20个线程会返回同一个实例对象
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HungerySingleton.getInstance());
}).start();
}
}
}
3)懒汉式(线程不安全)
/**
* 懒汉模式
* 1.线程安全性:多线程并发情况下不能保证实例对象的唯一性。
* 2.性能:实现了懒加载,性能相对饿汉式比较好。
*/
public class HoonSingleton {
private static HoonSingleton instance = null;
private HoonSingleton() {
}
// 在调用的过程中实例化对象并返回
public static HoonSingleton getInstance() {
// 在多线程情况下,一个线程进入了if (null == instance)判断语句块,还未来得及往下执
// 行,另一个线程也通过了这个判断语句,这时便会产生多个实例,
// 所以在多线程环境下不可使用这种方式。
if (null == instance) {
instance = new HoonSingleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HoonSingleton.getInstance());
}).start();
}
}
}
4)懒汉式(同步方法)
/**
* 懒汉模式 + 同步方法
* 1.线程安全性:通过增加synchronized保证了线程安全性。
* 2.性能:使用synchronized使得程序退化到了串行执行,降低了性能。
*/
public class HoonSingleton {
private static HoonSingleton instance = null;
private HoonSingleton() {
}
// 在调用的过程中实例化对象并返回(通过synchronized关键字修饰,保证线程安全性)
public synchronized static HoonSingleton getInstance() {
if (null == instance) {
instance = new HoonSingleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HoonSingleton.getInstance());
}).start();
}
}
}
5)懒汉式(同步代码块)
/**
* 懒汉模式(同步代码块)
* 不能保证线程安全性。
*/
public class HoonSingleton {
private static HoonSingleton instance = null;
private HoonSingleton() {
}
public static HoonSingleton getInstance() {
if (null == instance) {
// 多个线程进来的时候,不能保证线程安全性,可能会创建多个实例
synchronized (HoonSingleton.class) {
instance = new HoonSingleton();
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HoonSingleton.getInstance());
}).start();
}
}
}
6)DCL(Double-Check-Locking)
/**
* 懒汉模式 + DCL
* 1.线程安全性:通过synchronized以及DCL保证了线程安全性。
* 2.性能:使用懒加载以及DCL,性能比较好。
* 3.CPU在执行过程中有可能导致实例对象中的实例属性(对象)指令重排,引起空指针异常。
*/
public class HoonSingleton {
private static HoonSingleton instance = null;
private HoonSingleton() {
}
// DCL(Double-check-locking)
public static HoonSingleton getInstance() {
// 通过synchronized以及两次判断(DCL)保证线程安全性
if (null == instance) {
synchronized (HoonSingleton.class) {
if (null == instance) {
instance = new HoonSingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HoonSingleton.getInstance());
}).start();
}
}
}
通过volatile避免CPU指令重排导致空指针异常,进一步改良后的代码如下:
/**
* 懒汉模式 + DCL
* 1.线程安全性:通过synchronized以及DCL保证了线程安全性。
* 2.性能:使用懒加载以及DCL,性能比较好。
*/
public class HoonSingleton {
// 通过volatile避免CPU指令重排导致控制针异常
private volatile static HoonSingleton instance = null;
private HoonSingleton() {
}
// DCL(Double-check-locking)
public static HoonSingleton getInstance() {
// 通过synchronized以及两次判断(DCL)保证线程安全性
if (null == instance) {
synchronized (HoonSingleton.class) {
if (null == instance) {
instance = new HoonSingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(HoonSingleton.getInstance());
}).start();
}
}
}
7)Holder(静态内部类)
/**
* 持有者模式(Holder)
* 1.通过静态内部类实现懒加载,保证线程安全性,是目前应用比较广泛的一种单例模式
*/
public class HolderDemo {
private HolderDemo() {
}
private static class Holder {
private static HolderDemo instance = new HolderDemo();
}
// 懒加载
// synchronized
// <init>
// 调用这个方法时,内部类被装载,且只装载一次,所以时线程安全的
public static HolderDemo getInstance() {
return Holder.instance;
}
}
8)枚举式
public enum EnumDemo {
// 常量,在加载的时候被实例化,且只能实例化一次
A, B, C, D;
public static void m1() {
System.out.println("method");
}
public static void main(String[] args) {
A.m1();
B.m1();
C.m1();
D.m1();
System.out.println(A.getClass().getName());// 输出结果:EnumDemo
System.out.println(B.getClass().getName());// 输出结果:EnumDemo
System.out.println(C.getClass().getName());// 输出结果:EnumDemo
System.out.println(D.getClass().getName());// 输出结果:EnumDemo
}
}
改良代码:
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
改良
/**
* 同Holder(持有者模式)一样,是目前运用比较广泛的单例模式,大师结晶
*/
public enum EnumSingletonDemo {
private EnumSingletonDemo() {
}
private enum EnumHolder {
INSTANCE;
private EnumSingletonDemo instance;
EnumHolder() {
this.instance = new EnumSingletonDemo();
}
private EnumSingletonDemo getInstance() {
return instance;
}
}
public static EnumSingletonDemo getInstance() {
return EnumHolder.INSTANCE.instance;
}
}