【问题标题】:Java will synchronized block if changed?如果更改,Java 将同步阻塞?
【发布时间】:2019-10-10 09:16:32
【问题描述】:
private Object x = new Object();

public void doA() {
   synchronized(x){
       x = new Object();
       //do something
    }
}

public void doB() {
    synchronized(x) {
        //do something 
    }
}

假设doA()doB() 被同时调用,但doA() 首先进行。所以 B 将被阻止直到 doA() 完成

即使doA() 修改了x = new x 调用中的x,这是真的吗?或者在doA() 中的x = new x 这行之后doB() 将不再被阻止,因为x 已更改?

【问题讨论】:

  • 您始终可以尝试在本地计算机上运行此实验,可能使用长时间的睡眠和打印语句,以便您可以看到事情发生得足够慢,以便于理解。
  • 锁定在原始参考上。为什么会改变?
  • @yegodm 有道理
  • x 到底是什么?一类?一个领域? (构造函数?)
  • 无论如何,//do something 影响的状态最终不再由同一个锁保护,因此不存在有效的 happens-before 关系.

标签: java multithreading


【解决方案1】:

我自己的好奇心占了上风,我写了这个测试来验证会发生什么。锁定仍保留在原始引用上,不会更改。

import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class Test {

    private static Object x = new Object();

    public static void main(String[] args) throws Exception {
        Test a = new Test();
        Test b = new Test();

        Thread t = new Thread(a.new MyRunnable());
        t.start();
        Thread.sleep(100);
        b.doB();
    }

    public void doB() {
        System.out.println("here b");
        synchronized (x) {
            System.out.println("here sync b");
        }
        System.out.println("here done sync b");
    }

    public class MyRunnable implements Runnable {

        public MyRunnable() {
        }

        public void run() {
            System.out.println("here a");
            synchronized (x) {
                System.out.println("here sync a");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }

                x = new Object();

                System.out.println("here new object a");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("here after sleep a");
            }
            System.out.println("here done sync a");
        }
    }

}

输出如下。很明显,即使在重新分配之后,锁仍然保留在原始引用上。

here a
here sync a
here b
here new object a
here after sleep a
here done sync a
here sync b
here done sync b

【讨论】:

  • 这完全取决于 doB 是先获得 X 的新值还是旧值..所以即使你的测试有效..它只是有时有效..还取决于线程的优先级。 . 现在你正在使用一个优先级低于主线程的线程.. 如果 doB 在 doA 更改锁后捕获了锁的值,则代码将被搞砸..
  • 如果我们将 x 设为 volatile,那么我猜输出将会改变。完成新对象分配后,读取 x 变量的下一个线程应该会看到它的最新状态,这可能会导致不同的结果。
【解决方案2】:

会发生什么实际上取决于doA() 运行的速度:

如果doA()doB() 到达其同步块之前更改x 的值,则doB() 将锁定doA() 创建的 对象。

如果doB() 速度很快,并且在doA() 可以更改x 的值之前评估synchronized(x) 部分,那么doB() 将不得不等到doA()synchronized 块完成。

说明

每当 Java 在任一方法中访问 synchronized(x) 代码时,它都会计算变量 x,并且该变量会为 Java 提供一个对象。所以Java然后试图锁定那个对象,并保持锁定在那个对象上。或者,如果该对象上已经有锁,它将等待锁消失。 (每当 Java 到达synchronized(x) 代码时,它会获取变量的值并忽略变量本身,因此您可以在之后随意更改变量,但锁定和锁定检查仍然会在变量的先前值上发生。)

【讨论】:

  • 它比这稍微复杂一些,因为调用doB()的线程可能会或可能不会立即看到x的重新分配,具体取决于它是否为volatile
  • 所以在第一种情况下,由于doA()对新对象没有锁,所以doB()不会等待,但是doA()和doB()中没有x的冲突) 因为他们正在使用不同的 x
  • @ealeon 是的。 :) 不会发生等待,因为doB() 在开始运行时会看到一个新创建的未锁定对象。
  • 重申一下,除非xvolatile,否则调用doB() 的线程可能在第一个场景中看到一个新创建的、未锁定的对象。如果没有volatile 关键字,则不能保证线程可以看到另一个线程的操作。所以你可以让一个线程调用doB(),但仍然看到x 的旧值,另一个线程看到最新的值,第三个线程看到的值是三次重新分配前的值。这种线程间通信的现实是同步/波动性首先存在的原因——你需要一个 happens-before 关系。 @ealeon
  • @ealeon 根据给定的信息,我的建议是根本不这样做。它如何帮助您预防 CME?如果您需要迭代集合而不将其完全锁定,也许java.util.concurrent 中的集合类之一可以帮助您;它们具有“弱一致”的迭代器和拆分器。但是,如果您必须这样做,并且您依赖 x 始终是最新值,那么可以,添加 volatile 关键字。
【解决方案3】:

我认为 doA() 方法仍将锁定旧的“x”。除非线程持有完成处理同步块,否则不会通知等待锁定“x”的其他线程。 同步块完成后,线程将争夺 x 的新实例的锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-16
    • 2018-04-06
    • 2016-07-14
    • 1970-01-01
    • 2023-03-14
    • 2013-07-20
    • 2013-05-07
    相关资源
    最近更新 更多