简介
特点: 保证一个类只能有一个实例,且是自己实例化出来的,并提供一个全局访问点给外部类来获取该实例
优点:只有一个实例,节省内存
缺点:不易于拓展,多数情况下都要直接修改单例类
注意:单例模式构造方法为 private 权限,不能在其他类中使用 new 来创建;但可以使用反射来访问私有构造方法,为了安全考虑,可在构造方法体中抛出一个异常,避免使用反射来创建实例。
饿汉式
饿汉式是线程安全的,但不会延迟加载,且效率高
/**
* 饿汉式,线程安全的,效率高
*/
public class Singleton {
// 此处初始化已经保证了线程安全
private static Singleton singleton = new Singleton();
// 构造方法私有, 避免使用 new 创建多个实例
private Singleton() { }
// 提供给外部类去访问该单例(实例)的接口
public static Singleton getInstance() {
return singleton;
}
}
懒汉式
懒汉式可延迟实例代,是线程不安全的,需要在方法中加锁来保证线程安全,而每次访问该方法时,就要进行一次同步(加锁),这样会降低性能
/**
* 懒汉式,效率低
*/
public class Singleton {
private static Singleton singleton;
private Singleton() { }
// 要保证线程安全,需在此方法中加锁(synchronized、lock)
// public static synchronized Singleton getInstance() {
public static Singleton getInstance() {
if (singleton == null)
singleton = new Singleton();
return singleton;
}
}
双重检查加锁
双重检查加锁不像懒汉式那样一访问就加锁,而是先检查当前实例是否已经创建,如果未创建,再进行同步(加锁)来创建实例,这样只有第一次使用时才会进行同步,又能达到延迟加载;
注意:双重检查加锁不适用于 JDK 1.5 以下的版本
/**
* 双重校验锁,线程安全的
*/
public class Singleton {
// 使用 volatile 保证可见性
private volatile static Singleton singleton;
private Singleton() { }
// 使用再次判空检查,一次同步(加锁)来实现
public static Singleton getInstance() {
if (singleton == null)
synchronized (Singleton.class) {
// 此处判空是避免在并发情况下,有可能已经完成实例化操作
if (singleton == null)
singleton = new Singleton();
}
return singleton;
}
}
内部类
只有在访问 getInstance() 方法来获取实例的时候才会加载内部类,达到延迟加载的效果,且是线程安全的
/**
* 静态内部类, 延迟加载
*/
public class Singleton {
private Singleton() { }
// 内部类
private static class InnerClass {
private static final Singleton SINGLETON = new Singleton();
}
public static final Singleton getInstance() {
return InnerClass.SINGLETON;
}
}
枚举
线程安全,但没有延迟加载,可以防止反射
/**
* 枚举,线程安全
*/
public enum Singleton {
INSTANCE;
// 其他方法
public void operation() {
System.out.println(".........");
}
}
防止反射示例
在构造方法中抛出异常,可防止使用反射创建实例
/**
* 防止反射
*/
public class Singleton {
private static Singleton instance = new Singleton();
// 抛出一个异常
private Singleton() {
if (instance != null)
throw new RuntimeException();
}
public static Singleton6 getInstance() {
return instance;
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) throws Exception {
Class<Singleton> clazz = (Class<Singleton>) Class.forName("com.zlk.singleton.Singleton");
Constructor<Singleton> constructor = clazz.getDeclaredConstructor(null);
// 设置可以访问私有权限方法
constructor.setAccessible(true);
Singleton instance1 = constructor.newInstance();
Singleton instance2 = constructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(Singleton.getInstance());
}
}