java final 并发_Java并发拾遗(五)——final

本文探讨了Java中final关键字在并发环境中的作用,如何保证内存可见性和防止重排序。通过一个实例展示了当final变量在构造函数中初始化时,如何确保其他线程在访问对象时看到的是完整初始化的final域。理解final的这一特性对于编写线程安全的代码至关重要。
摘要由CSDN通过智能技术生成

final也是并发中一个常用的tips。JMM中对final提供的重排序规则如下:

1、当在constructor中对final变量写入之前,不会将构造对象的引用给出去。

2、读一个包含final域的对象的引用 happens before 后续对这个final域的读

关于(1),是说当在constructor内包含对final的写时,虽然constructor不是原子的,但在JMM会在对final的写之后,在constructor return之前插入一条内存屏障,这样就保证了在constructor将对象给到另一个线程的时候,是一定能看见对final域的写的结果的。看代码:

public class FinalExample {

int i;

final int j;

static FinalExample obj;

// constructor

public void FinalExample () {

i = 1;

j = 2;

}

public static void writer () {

obj = new FinalExample();

}

public static void reader () {

FinalExample object = obj;

int a = object.i;

int b = object.j;

}

}

现假设一个线程A执行writer方法,另一个线程B随后开始执行read方法。假设变量j不是final的,那么A在调用write时,constructor会发生两次写操作,这个时候线程B来调用reader读字段的时候,由于constructor方法不是原子的,又没有任何同步手段来协调线程A与B的同步,这时候就会乱掉了,比如constructor两次赋值发生了重排序,或者只完成了一次赋值,线程B就拿到构造一半的对象进行读值了,等等并发问题了。

但是,当把j变量定义为final时,就会发生一点点的变化了。

(1)final能够保证,在对象引用被任意线程可见之前,对象的final域一定被正确初始化了。而实现这种保证的手段,就是通过内存屏障,保证final域的赋值不会被重排序到constructor return之后。

(2)final能够保证,初次读对象引用于初次读该对象所包含的final域,不会发生重排序。所以在执行read方法时,final能够保证一定是先确实拿到了obj的引用之后,才能读obj中的final域j。

而实际上,IDEA对于final的赋值只能在final类变量定义时,不能将final的赋值推迟到constructor里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值