正文
常理上,用final修饰的变量具有内存可见性的特点,而且是不会变化的,可以保证他是不可变的。但是,仅仅是在是在一个不可变的对象在正确的创建的出来,才是线程安全的。
那什么时候,是不正确的创建方式呢?答案就是:在创建的过程中,有没有发生this的引用逃逸的情况。
引用逃逸:在构造器构造还未彻底完成前(即实例初始化阶段还未完成),将自身this引用向外抛出并被其他线程复制(访问)了该引用,可能会问到该还未被初始化的变量,甚至可能会造成更大严重的问题。(发生的概率较小,但是并不表示不会发生)。
输出:这说明ThisEscape还未完成实例化,构造还未彻底结束。
发生空指针错误:普通变量j未被初始化 发生空指针错误:final变量i未被初始化
另一种情况是利用线程A模拟this逃逸,但不一定会发生(小概率事件一定会发生的),线程A模拟构造器正在构造...而线程B尝试访问变量,这是因为
(1)由于JVM的指令重排序存在,实例变量i的初始化被安排到构造器外(final可见性保证是final变量规定在构造器中完成的);
(2)类似于this逃逸,线程A中构造器构造还未完全完成。
如下面的代码:
所以,用final修饰的对象并不一定线程安全的,任何的代码所说的安全都是在一定范围、一定条件下所说的线程安全。