目录
模式定义
保证一个类只有一个实例,并且提供一个全局访问点
应用场景
重量级的对象,不需要多个实例,如线程池,数据库连接池
实现方式
1.懒汉模式:
延迟加载 ,只有在真正使用的时候,才开始实例化。
1)线程安全问题。
2)double check 加锁优化。
3)编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile 关键字进行修饰,对于volatile 修饰的字段,可以防止指令重排。
java
public class LazySingletonTest {
public static void main(String[] args) {
// LazySingleton instance1 = LazySingleton.getInstance();
// LazySingleton instance2 = LazySingleton.getInstance();
// System.out.println(instance1==instance2); //true
new Thread(() -> {
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance1);
}).start();
new Thread(() -> {
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance2);
}).start();
}
}
class LazySingleton {
private volatile static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class){
if (instance == null) {
instance = new LazySingleton();
// 字节码层
// JIT , CPU 有可能对如下指令进行重排序
// 1. 分配空间 2. 初始化 3. 引用赋值
}
}
}
return instance;
}
}
2.饿汉模式:
类加载的初始化阶段就完成了实例的初始化 。
本质上就是借助于jvm类加载机制 ,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。
类加载过程:
- 加载二进制数据到内存中, 生成对应的Class数据结构,
- 连接: a. 验证, b.准备(给类的静态成员变量赋默认值),c.解析
- 初始化: 给类的静态变量赋初值
只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即main函数所在类,直接进行new 操作,访问静态属性、访问静态方法,用反射访问类,初始化一个类的子类等.)
java
public class HungrySingletonTest {
public static void main(String[] args) {
HungrySingleton instance = HungrySingleton.getInstance();
HungrySingleton instance1 = HungrySingleton.getInstance();
System.out.println(instance==instance1);
}
}
class HungrySingleton {
private static HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return instance;
}
}
3.静态内部类
1).本质上是利用类的加载机制 来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。
java
public class InnerClassSingletonTest {
public static void main(String[] args) {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true
new Thread(() -> {
InnerClassSingleton instance = InnerClassSingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
System.out.println(instance1);
}).start();
}
}
class InnerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton(){
}
public static InnerClassSingleton getInstance(){
return InnerClassHolder.instance;
}
}
反射
java
public class InnerClassSingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true
// new Thread(() -> {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(instance);
// }).start();
//
// new Thread(() -> {
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance1);
// }).start();
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
InnerClassSingleton instance = InnerClassSingleton.getInstance();
System.out.println(innerClassSingleton==instance); //false
}
}
class InnerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton(){
}
public static InnerClassSingleton getInstance(){
return InnerClassHolder.instance;
}
}
如何防止反射攻击破坏?
饿汉模式下无法避免,懒汉模式可在构造函数中增加如下代码,判断实例是否存在。
java
private InnerClassSingleton(){
if (InnerClassHolder.instance!=null){
throw new RuntimeException("单例不允许多个实例");
}
}
java
public class InnerClassSingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true
// new Thread(() -> {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(instance);
// }).start();
//
// new Thread(() -> {
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance1);
// }).start();
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
InnerClassSingleton instance = InnerClassSingleton.getInstance();
System.out.println(innerClassSingleton==instance); //false
}
}
class InnerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton(){
if (InnerClassHolder.instance!=null){
throw new RuntimeException("单例不允许多个实例");
}
}
public static InnerClassSingleton getInstance(){
return InnerClassHolder.instance;
}
}
枚举类型
1)天然不支持反射创建对应的实例 ,且有自己的反序列化机制
2)利用类加载机制保证线程安全
java
public enum EnumSingletonTest {
INSTANCE;
private void print(){
System.out.println(this.hashCode());
}
}
class EnumTest{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
EnumSingletonTest instance = EnumSingletonTest.INSTANCE;
// EnumSingletonTest instance1 = EnumSingletonTest.INSTANCE;
// System.out.println(instance==instance1); // true
// Constructor<EnumSingletonTest> declaredConstructor = EnumSingletonTest.class.getDeclaredConstructor(String.class,int.class);
// declaredConstructor.setAccessible(true);
// EnumSingletonTest instance = declaredConstructor.newInstance("INSTANCE",0);
// ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("enumsingleton"));
// oss.writeObject(instance);
// oss.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enumsingleton"));
EnumSingletonTest object = ((EnumSingletonTest) ois.readObject());
System.out.println(instance==object); //true:有自己的反序列化机制
}
}
序列化
可以利用指定方法readResolve来替换从反序列化流中的数据 如下
java
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANY‐ACCESS‐MODIFIER Object readResolve() throws ObjectStreamException;
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
java
public class InnerClassSingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true
// new Thread(() -> {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(instance);
// }).start();
//
// new Thread(() -> {
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance1);
// }).start();
// Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
// declaredConstructor.setAccessible(true);
// InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(innerClassSingleton==instance); //false
InnerClassSingleton instance = InnerClassSingleton.getInstance();
// ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("testSerializable"));
// oss.writeObject(instance);
// oss.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
InnerClassSingleton object = ((InnerClassSingleton) ois.readObject());
System.out.println(instance==object);
}
}
class InnerClassSingleton implements Serializable {
static final long serialVersionUID =42L;
private static class InnerClassHolder{
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton(){
if (InnerClassHolder.instance!=null){
throw new RuntimeException("单例不允许多个实例");
}
}
public static InnerClassSingleton getInstance(){
return InnerClassHolder.instance;
}
Object readResolve() throws ObjectStreamException{
return InnerClassHolder.instance;
}
}
部分源码中的应用定位
感兴趣的可以去官网下载源码深入研究
Spring & JDK
java
java.lang.Runtime
java
org.springframework.aop.framework.ProxyFactoryBean
java
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
java
org.springframework.core.ReactiveAdapterRegistry
Tomcat
java
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory
反序列化指定数据源
java
java.util.Currency
创作不易,点个关注吧!!!
创作不易,点个关注吧!!!
创作不易,点个关注吧!!!