深入思考系列——"Synchronization on a non-final field"

场景

程序里面使用了synchronized关键字,IntelliJ IDEA右边出现了黄色条,移动到上面,提示如下:
在这里插入图片描述

环境

软件版本
JDK1.8
IntelliJ IDEA2019.1

原因

从提示来看,就是说,如果变量的引用发生了改变,就会导致synchronized失效,然后其他线程就会进入原本没有结束的synchronized代码块。所以要使用final来修饰变量,使引用不发生改变。

尝试

接下来让我们来验证一下,是否真的如提示所说。

错误范例

我写了以下代码,请各位读者看:

public class Test1 implements Runnable{
    private Byte[] flag = new Byte[1];
    
    @Override
    public void run() {
        synchronized (flag){
            System.out.println(new Date()+" 开始休眠"+Thread.currentThread().getName());
            flag = new Byte[11];
            try {
                Thread.sleep(10000);

                System.out.println(new Date()+" 结束休眠"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Test1 test = new Test1();
        new Thread(test).start();
        Thread.sleep(100);
        new Thread(test).start();
    }
}

如果synchronized关键字有生效的话,就会导致前后两个线程会间隔10秒打印日志。结果如下:

Fri Apr 10 22:03:43 CST 2020 开始休眠Thread-0
Fri Apr 10 22:03:43 CST 2020 开始休眠Thread-1
Fri Apr 10 22:03:53 CST 2020 结束休眠Thread-0
Fri Apr 10 22:03:53 CST 2020 结束休眠Thread-1

从结果可以看到,中间两个线程基本没有间隔就开始了。和我们的预期是存在差异的。那是什么原因导致了变量的引用发生了改变吗?就是代码里面的那行重新赋值的语句:

flag = new byte[11];

所以,也正如提示所说的,就是因为变量的引用发生了改变,所以导致了synchronized关键字没有生效。接下来就让我们开始正确的尝试阶段。

正确范例

我对错误范例里面的代码进行优化,在这里主要按照提示所说,加上final来使得对象不必改变,各位读者跟随我的思路来。

public class Test1 implements Runnable{
    private final Byte[] flag = new Byte[1];

    @Override
    public void run() {
        synchronized (flag){
            System.out.println(new Date()+" 开始休眠"+Thread.currentThread().getName());
            flag[0] = 11;
            // 因为变量设置为了final,没有办法进行修改。故注释掉。
			//flag = new Byte[11];
            try {
                Thread.sleep(10000);

                System.out.println(new Date()+" 结束休眠"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Test1 test = new Test1();
        new Thread(test).start();
        Thread.sleep(100);
        new Thread(test).start();
    }
}

结果如下:

Sun Apr 12 00:25:33 CST 2020 开始休眠Thread-0
Sun Apr 12 00:25:43 CST 2020 结束休眠Thread-0
Sun Apr 12 00:25:43 CST 2020 开始休眠Thread-1
Sun Apr 12 00:25:53 CST 2020 结束休眠Thread-1

从结果来看,这个时候的synchronized是已经生效了。因为加了final,导致变量无法被改变引用。

总结

并发情况下面,一个变量如果是打算作为锁,就得考虑是否会被更新引用的情况。所以,并发情况下,还是要多思考,才能写出质量上乘的代码。

随缘求赞

如果我的文章对大家产生了帮忙,可以在文章底部点个赞或者收藏;
如果有好的讨论,可以留言;
如果想继续查看我以后的文章,可以左上角点击关注

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫夜求索阁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值