标签
单例
字数
2341 字
阅读时间
10 分钟
单例设计模式
作用及好处:
在应用程序中保证最多只有一个实例对象,提高运行效率,实现共享数据。
**饿汉特点:**在获取单例对象之前对象已经创建完成了。
**懒汉特点:**在真正需要单例的时候才创建出该对象。在Java程序中,有时候可能需要推迟一些高开销对象的初始化操作,并且只有在使用这些对象的时候才初始化,此时,程序员可能会采用延迟初始化
饿汉式(静态常量)
java
//解决了懒汉式中多线程访问可能出现同一个对象和效率低问 题
public class SingleTon {
//在类加载时进行实例化.
private static SingleTon singleton=new SingleTon();
private SingleTon(){}
public static SingleTon getInstance(){
return singleton;
}
}饿汉式(静态代码块)
java
/**
目标:饿汉式(静态代码块)
步骤:
1.构造器私有。
2.定义一个静态常量保存一个唯一的实例对象(单例),可以通过静态代码块初始化单例对象。
3.提供一个方法返回单例对象。
*/
public class Singleton02 {
// 2.定义一个静态常量保存一个唯一的实例对象(单例)
private static final Singleton02 INSTANCE ;
static{
INSTANCE = new Singleton02();
}
// 1.构造器私有。
private Singleton02(){
}
// 3.提供一个方法返回单例对象。
public static Singleton02 getInstance(){
return INSTANCE;
}
}懒汉式 (线程不安全)
java
/**
目标:懒汉式(线程不安全的写法)。
步骤:
1.构造器私有。
2.定义一个静态的变量存储一个单例对象(定义的时候不初始化该对象)
3.定义一个获取单例的方法,每次返回单例对象的时候先询问是否有对象,有直接返回。
没有就创建一个新的单例对象。
*/
public class Singleton03 {
// 2.定义一个静态的变量存储一个单例对象(定义的时候不初始化该对象)
private static Singleton03 INSTANCE;
// 1.构造器私有。
private Singleton03(){
}
// 3.定义一个获取单例的方法,每次返回单例对象的时候先询问是否有对象,有直接返回。
// 没有就创建一个新的单例对象。
public static Singleton03 getInstance(){
if(INSTANCE == null){
// 说明这是第一次来拿单例对象,需要真正的创建出来!
INSTANCE = new Singleton03();
}
return INSTANCE;
}
}懒汉式(线程安全,性能差)
使用synchronized关键字修饰方法包装线程安全,但性能差多,并发下只能有一个线程正在进入获取单例对象。
java
/**
目标:懒汉式(线程安全的写法)。
步骤:
1.构造器私有。
2.定义一个静态的变量存储一个单例对象(定义的时候不初始化该对象)
3.定义一个获取单例的方法,每次返回单例对象的时候先询问是否有对象,有直接返回。
没有就创建一个新的单例对象。
4.为获取单例的方法加锁:用synchronized
*/
public class Singleton04 {
// 2.定义一个静态的变量存储一个单例对象(定义的时候不初始化该对象)
private static Singleton04 INSTANCE;
// 1.构造器私有。
private Singleton04(){
}
// 3.定义一个获取单例的方法,每次返回单例对象的时候先询问是否有对象,有直接返回。
// 没有就创建一个新的单例对象。
// 懒汉式线程安全的写法:线程A , 线程B.
public synchronized static Singleton04 getInstance(){
if(INSTANCE == null){
// 说明这是第一次来拿单例对象,需要真正的创建出来!
INSTANCE = new Singleton04();
}
return INSTANCE;
}
}懒汉式(线程不安全)
特点:是一种优化后的似乎线程安全的机制
java
/**
目标:懒汉式(线程不安全)
步骤:
1.构造器私有。
2.定义一个静态变量存储一个单例对象。
3.提供一个方法返回一个单例对象。
*/
public class Singleton05 {
// 2.定义一个静态变量存储一个单例对象。
private static Singleton05 INSTANCE ;
// 1.构造器私有
private Singleton05(){
}
// 3.返回一个单例对象
public static Singleton05 getInstance(){
// 判断单例对象的变量是否为null
if(INSTANCE == null){
// 很多个线程执行到这里来:A , B
synchronized (Singleton05.class){
INSTANCE = new Singleton05();
}
}
return INSTANCE;
}
}懒汉式(volatile双重检查模式,推荐)
双重检查的优点:线程安全,延迟加载,效率较高!
使用volatile保证安全
禁止指令重排序
new Singleton()是一个非原子操作,编译器可能会重排序【构造函数可能在整个对象初始化完成前执行完毕,即赋值操作(只是在内存中开辟一片存储区域后直接返回内存的引用)在初始化对象前完成】。而线程C在线程A赋值完时判断instance就不为null了,此时C拿到的将是一个没有初始化完成的半成品。这样是很危险的。因为极有可能线程C会继续拿着个没有初始化的对象中的数据进行操作,此时容易触发“NPE异常
保证可见性。
由于可见性问题,线程A在自己的工作线程内创建了实例,但此时还未同步到主存中;此时线程C在主存中判断instance还是null,那么线程C又将在自己的工作线程中创建一个实例,这样就创建了多个实例。 如果加上了volatile修饰instance之后,保证了可见性,一旦线程A返回了实例,线程C可以立即发现Instance不 为null。
java
/**
目标:双重检查机制,以及使用volatile修饰(最好,最安全的方式,推荐写法)
步骤:
1.构造器私有。
2.提供了一个静态变量用于存储一个单例对象。
3.提供一个方法进行双重检查机制返回单例对象。
4.必须使用volatile修饰静态的变量。?
双重检查的优点:线程安全,延迟加载,效率较高!!
*/
public class Singleton06 {
// 2.提供了一个静态变量用于存储一个单例对象。
private volatile static Singleton06 INSTANCE;
// 1.构造器私有。
private Singleton06(){
}
// 3.提供一个方法进行双重检查机制返回单例对象。
public static Singleton06 getInstance(){
// 第一次检查:判断单例对象的变量是否为null
if(INSTANCE == null ){
// A , B
synchronized (Singleton06.class){
// 第二次检查:判断单例对象的变量是否为null
if(INSTANCE == null){
INSTANCE = new Singleton06();
}
}
}
return INSTANCE;
}
}静态内部类单例方式
JVM在类初始化阶段(即在Class被加载后,且线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。
- 静态内部类是在被调用时才会被加载,这种方案实现了懒汉单例的一种思想,需要用到的时候才去创建单例,加上JVM的特性,这种方式又实现了线程安全的创建单例对象。
- 通过对比基于volatile的双重检查锁定方案和基于类初始化方案的对比,我们会发现基于类初始化的方案的实现代码更简洁。但是基于volatile的双重检查锁定方案有一个额外的优势:除了可以对静态字段实现延迟加载初始化外,还可以对实例字段实现延迟初始化。
java
/**
目标:基于类的初始化实现延迟加载和线程安全的单例设计。
步骤:
1.构造器私有。
2.提供一个静态内部类,里面提供一个常量存储一个单例对象。
3.提供一个方法返回静态内部类中的单例对象。
*/
public class Singleton07 {
// 1.构造器私有。
private Singleton07(){
}
// 2.提供一个静态内部类,里面提供一个常量存储一个单例对象。
private static class Inner{
private static final Singleton07 INSTANCE = new Singleton07();
}
// .提供一个方法返回静态内部类中的单例对象。
// 线程A , 线程B
public static Singleton07 getInstance(){
return Inner.INSTANCE;
}
}枚举实现单例
java
/**
目标:枚举实现单例。
引入:枚举实际上是一种多例的模式。如果我们直接定义一个实例就相当于是单例了。
*/
public enum Singleton08 {
INSTANCE;
}