首页 > 解决方案 > 如果重新分配对同步块内锁定对象的引用会怎样?

问题描述

注意:无效问题 - 请参阅@Bukhtoyarov Vladimir 的评论

假设我们有以下代码:

public class Main {
    private Object monitor = new Object();

    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        main.test();
        new Thread() {
            @Override
            public void run() {
                try {
                    main.changeMonitor();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private void test() throws InterruptedException {
        synchronized (monitor) {
            Thread.sleep(100);
            monitor = new Object();
            Thread.sleep(1000);
            System.out.println("test finished");

        }
    }

    private void changeMonitor() throws InterruptedException {
        Thread.sleep(600);
        monitor = new Object();
        System.out.println("monitor changed");
    }
}

这里我们有两个线程——主线程和另一个工作线程。我们也有monitor对象。在工作线程内部,我们有下一个动作序列 -

在主线程中,我们等待 600 毫秒并尝试将监视器重新分配给新对象。结果 - 主线程被阻塞 - 直到工作线程释放monitor对象上的锁定。在这里我有两个问题

  1. 根据Concurrency in practice书本——被锁获取过程阻塞的唯一方法——是进入同步块。那么为什么主线程被阻塞直到工作线程释放锁 - 在主线程中我们并没有尝试进入同步块
  2. 工作线程monitor在 100 毫秒后将新对象分配给引用,为什么主线程在 600 毫秒后无法获取新重新分配对象的锁定?我的意思是 - 在 600 毫秒后ref 是新对象 - 所以应该准备好获得锁 行为很有趣 - 因为我在官方 Oracle 文档或书中monitor找不到任何关于它的信息。Concurrency in practice

标签: javaconcurrency

解决方案


这段代码

 synchronized (monitor) {

就好像

Object m = monitor;
synchronized (m) {

即只读发生一次,并且在不是线程安全的上下文中。

为什么主线程不能锁定新对象 - 在工作线程内重新分配。

这表示

  • 一旦获得要锁定的对象,它就不会继续读取循环中的最新值以查看它是否可以锁定另一个对象。
  • 即使在读取之前更改了引用,它也可以看到旧值,因为读取不是线程安全的。

推荐阅读