JVM:浅谈静态代码块和<clinit>方法

2024-09-17 11:16:38 浏览数 (1)

概念

静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

理论依据是<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>()方法会被正确加锁、同步的执行一次

0 人点赞