设计模式之单例模式
单例模式(Singleton)是设计模式一种,属于建造型的模式。
单例模式,顾名思义就是只有一个实例,该类确保单个对象被创建,可以直接访问,不需要实例化该类的对象,以下是几种创建单例模式的方式
饿汉式
特点
- 私有化构造方法
- 类加载到内存的时候初始化(类只会加载一次(双亲委派机制),保证实例唯一)
- 提供对外获取实例的静态方法
- JVM保证线程安全
- 比较常用,但容易产生垃圾,因为一开始就初始化
第1种写法
- 静态对象,类加载时候初始化
public class Singleton1 {
private static final Singleton1 INSTANCE= new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
private Singleton1() {
}
}
第2种写法
- 静态块初始化对象,和第1种一个意思
public class Singleton2 {
private static final Singleton2 INSTANCE;
static {
INSTANCE= new Singleton2();
}
private Singleton2() {
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
懒汉式
特点
- 私有化构造方法
- 使用的时候才会初始化
- 提供对外获取实例的静态方法
第1种写法
- 使用静态内部类的方式,JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载,线程安全
public class Singleton1 {
private static class Singleton1Holder {
private static final Singleton1 INSTANCE= new Singleton1();
}
public static Singleton1 getInstance() {
return Singleton1Holder.INSTANCE;
}
private Singleton1() {
}
}
第2种写法
- 虽然达到了按需加载的方式,但是线程不安全
public class Singleton2 {
private static Singleton2 INSTANCE;
private Singleton2 () {
}
public static Singleton2 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton2 ();
}
return INSTANCE;
}
}
第3种写法
- 使用synchronized关键字,可以保证线程安全性,但是上来就加锁,锁粒度高,效率低
public class Singleton3 {
private static Singleton3 INSTANCE;
private Singleton3() {
}
public static synchronized Singleton3 getInstance() {
if (INSTANCE == null) {
return new Singleton3();
}
return INSTANCE;
}
}
第4种写法
- 同样使用synchronized关键字,但做了双重检查(DCL),线程安全,实现延迟初始化,多线程情况下能保持高性能
public class Singleton4 {
private static volatile Singleton4 INSTANCE;
private Mgr06() {
}
public static Singleton4 getInstance() {
if (INSTANCE == null) {
//双重检查
synchronized (Singleton4.class) {
if(INSTANCE == null) {
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
}
- 第一次判断
INSTANCE == null
,为了不必要的加锁,提高性能,如果有一个线程已经创建了,就直接返回该对象 - 第二次判断
INSTANCE == null
,为了进行同步,避免多线程问题 INSTANCE = new Singleton4()
对象的创建在JVM中可能会进行重排序,多线程情况下,创建的对象可能是半初始化状态,使用volatile
关键字可以禁止指令重排,解决该问题
枚举模式
特点
- 枚举类隐藏了私有的构造器。
- 枚举类的域 是相应类型的一个实例对象
- 防止反序列化
- 线程安全
- 《Effective Java》书中推荐的单例模式,日常开发中很少用
写法
public enum Singleton1{
INSTANCE;
}
- Disruptor单机最快MQ,使用枚举单例来创建守护线程
package com.lmax.disruptor.util;
import java.util.concurrent.ThreadFactory;
/**
* Access to a ThreadFactory instance. All threads are created with setDaemon(true).
*/
public enum DaemonThreadFactory implements ThreadFactory
{
INSTANCE;
@Override
public Thread newThread(final Runnable r)
{
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
}
总结
- 日常开发中 一般采用饿汉式,若对资源十分在意可以采用静态内部类(既保证懒加载又保证安全性),不建议采用懒汉式及双重检测