不能忽视的 Synchronization on a non-final field

Synchronization on a non-final field

今天在做一个多线程试验的时候,对一个变量使用 synchronized ,出现了这样的提示 “Synchronization on a non-final field XXX”,查看详细文档可以看到下面这段话:

Reports synchronized statements where the lock expression is a reference to a non-final field. Such statements are unlikely to have useful semantics, as different threads may be locking on different objects even when operating on the same object.

百度翻译:报告同步语句,其中锁表达式是对非最终字段的引用。这样的语句不太可能具有有用的语义,因为即使在同一对象上操作,不同的线程也可能锁定不同的对象。

一、解释

官方注释的解释大概意思是我们对一个变化的对象加锁,如果这个变量的引用发生了改变,不同的线程可能锁定不同的对象,都会成功获得各自的锁。

二、尝试

为了进一步弄明白这段话的意义,进行了一个小试验,写了如下代码。

private static String flag = "";

public static class Task implements Runnable {

    private final int id;

    public Task(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        synchronized (flag) {
            System.out.println(String.format("Thread-%d start.", this.id));
            flag = "123";
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(String.format("Thread-%d end.", this.id));
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    new Thread(new Task(1)).start();
    Thread.sleep(100);
    new Thread(new Task(2)).start();
}

我的目的是想让两个线程分别有序执行,第一个线程结束后第二个线程再开始,于是锁定了一个 flag ,这是一个变量。
打印如下:

在这里插入图片描述

可以看到两个线程几乎同时开始执行,也就是文档中的那句话,两个线程锁定到了不同的对象,所以与我们预想的完全不同。

三、修改

我采用了一个数组的方式来存放我们要修改的对象,且用 final 修饰 ,这样可以有效避免被锁对象的引用发送转变。其实也可以用其他对象包装。

private static final String[] flag = {""};

public static class Task implements Runnable {

    private final int id;

    public Task(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        synchronized (flag) {
            System.out.println(String.format("Thread-%d start.", this.id));
            flag[0] = "123";
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(String.format("Thread-%d end.", this.id));
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    new Thread(new Task(1)).start();
    Thread.sleep(100);
    new Thread(new Task(2)).start();
}

输出结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值