单例模式:确保一个类只有一个实例,并提供一个全局访问点。不管是采用懒汉式,还是饿汉式的实现方式,这个全局访问点是一样的。
单例模式的范围:目前Java里面实现的单例是一个虚拟机的范围。因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现单例类的时候就会创建一个类的实例。当一个虚拟机中有多个ClassLoader,而且这些ClassLoader都装载某个类的话,就算这个类是单例,也会产生很多个实例。当一个机器上有多个虚拟机,那么每个虚拟机里面都应该至少有一个这个类的实例。
单例模式的调用顺序示意图:
![](https://img.haomeiwen.com/i9248921/23520111fb664461.png)
![](https://img.haomeiwen.com/i9248921/ad150ace31a40b97.png)
饿汉式
饿汉式实现示例代码如下:
/**
* 饿汉式
* 类加载到内存后,就实例化一个单例,JVM保证线程安全
* 简单实用,推荐使用!
* 唯一缺点:不管用到与否,类装载时就完成实例化
*/
public class Singleton01 {
/**
* 定义一个私有静态变量用来存储创建好的类实例,保证了线程安全
*/
private static Singleton01 INSTANCE = new Singleton01();
/**
* 定义一个私有的类构造器,防止外部创建类实例,可以在内部控制类实例的数目
*/
private Singleton01(){}
/**
* 定义一个静态方法,为客户端提供访问类实例的方法
* @return
*/
public static Singleton01 getInstance(){
return INSTANCE;
}
}
饿汉式:在装载类的时候就创建了对象实例,因此,JVM保证了线程安全。
缺点:不管用不用,类装载时就完成了实例化。
懒汉式(线程不安全)
懒汉式实现示例代码如下:
/**
* lazy loading
* 也称懒汉式
* 虽然达到了按需初始化的目的,但却带来线程不安全的问题
*/
public class Singleton02 {
/**
* 定义一个私有静态变量,用来存储类实例
*/
private static Singleton02 INSTANCE;
/**
* 定义一个私有构造器,防止外部创建类实例,可以在内部创建类实例的数目
*/
private Singleton02() {}
/**
* 定义一个静态方法,为客户端提供访问类实例的方法,线程不安全
* @return
*/
public static Singleton02 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton02();
}
return INSTANCE;
}
}
懒汉式:在装载对象的时候不创建对象实例,当第一次使用的时候,才会创建实例,在多线程情况下,判断INSTANCE == null会导致线程安全问题。
懒汉式(线程安全)
懒汉式实现示例代码如下:
/**
* lazy loading
* 也称懒汉式
*/
public class Singleton03 {
/**
* 定义一个静态变量,用来存储类实例
*/
private static Singleton03 INSTANCE;
/**
* 定义一个私有的构造器,在内部控制创建类实例的数目
*/
private Singleton03() {}
/**
* 用synchronized关键字定义一个静态方法,为客户端提供访问类实例的方法,线程安全,但也效率低下
* @return
*/
public static synchronized Singleton03 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton03();
}
return INSTANCE;
}
}
通过增加synchronized关键字到getInstance()方法中,解决了懒汉式的线程安全问题,但同时导致了效率低下。
懒汉式(双重检查加锁)
懒汉式实现示例代码如下:
/**
* lazy loading
* 也称懒汉式(双重检查加锁),在getInstance() 中减少使用同步
*/
public class Singleton05 {
/**
* volatile关键字确保在多个线程下正确的处理INSTANCE变量
*/
private static volatile Singleton05 INSTANCE; //JIT
/**
* 创建私有的构造器,在内部控制创建类实例的数目
*/
private Singleton05() {}
/**
* 用双重检查加锁的方式,为客户端提供访问类实例的方法,保证了线程安全,减少使用同步区块
* @return
*/
public static Singleton05 getInstance() {
// 检查实例,若不存在,就进入同步区块
if (INSTANCE == null) {
synchronized (Singleton05.class) {
// 进入同步区块后,在检查一次,如果仍为null,才创建实例
if(INSTANCE == null) {
INSTANCE = new Singleton05();
}
}
}
return INSTANCE;
}
}
静态内部类
静态内部类实现示例代码如下:
/**
* 静态内部类方式
* JVM保证单例
* 加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class Singleton06 {
/**
* 定义私有构造器
*/
private Singleton06() {}
/**
* 定义私有静态内部类
*/
private static class Mgr06Holder {
private static Singleton06 INSTANCE = new Singleton06();
}
/**
* 为客户端提供访问类实例的方法
* @return
*/
public static Singleton06 getInstance() {
return Mgr06Holder.INSTANCE;
}
}
枚举
枚举实现实例代码如下:
/**
* 不仅可以解决线程同步,还可以防止反序列化。
*/
public enum Singleton07 {
INSTANCE;
}
网友评论