概念
静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
理论依据是<clinit>方法:<clinit>()方法是编译器给某些类(带有静态代码块,或者静态变量、静态内部类)自动生成的方法,目的是,确保静态语句块有序顺序的执行,并且只会执行一次。
下面是取材于《深入理解JVM虚拟机》第7章-7.3.5节的内容:
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。
测试demo
代码语言:txt复制package com.bryant.singleton;
public class TestClinitMethod {
// 静态代码块,有且只会被JVM编译执行一次
static {
if (true) {
System.out.println(Thread.currentThread() "init TestClinitMethod, check out whether static code block can be executed several times...");
}
}
public static void main(String[] args) {
Runnable runnable = new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread() " start...");
TestClinitMethod testClinitMethod = new TestClinitMethod();
System.out.println(Thread.currentThread() " end...");
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}
日志打印:
代码语言:txt复制Thread[main,5,main]init TestClinitMethod, check out whether static code block can be executed several times...
Thread[Thread-0,5,main] start...
Thread[Thread-0,5,main] end...
Thread[Thread-1,5,main] start...
Thread[Thread-1,5,main] end...
验证理论:
- static {}静态代码块有且只被执行一次
- 即使多线程下,同个类被new一次,虚拟机会保证一个类的<clinit>()方法会被正确加锁、同步的执行一次