如果重新分配对同步块内的锁定对象的引用将是什么?

时间:2018-12-12 09:04:37

标签: java concurrency

注意:问题无效-请参阅@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对象。在工作线程中,我们有下一个操作序列-

  • 获取对monitor的锁定
  • 等待100毫秒
  • 分配监视器引用以指向新对象
  • 再等待1000毫秒

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

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

1 个答案:

答案 0 :(得分:5)

此代码

 synchronized (monitor) {

就像

Object m = monitor;
synchronized (m) {

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

  

为什么主线程无法锁定新对象-在工作线程中重新分配。

这是

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