java - 如果重新分配对同步块内锁定对象的引用会怎样?
问题描述
注意:无效问题 - 请参阅@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
对象上的锁定。在这里我有两个问题
- 根据
Concurrency in practice
书本——被锁获取过程阻塞的唯一方法——是进入同步块。那么为什么主线程被阻塞直到工作线程释放锁 - 在主线程中我们并没有尝试进入同步块 - 工作线程
monitor
在 100 毫秒后将新对象分配给引用,为什么主线程在 600 毫秒后无法获取新重新分配对象的锁定?我的意思是 - 在 600 毫秒后ref 是新对象 - 所以应该准备好获得锁 行为很有趣 - 因为我在官方 Oracle 文档或书中monitor
找不到任何关于它的信息。Concurrency in practice
解决方案
这段代码
synchronized (monitor) {
就好像
Object m = monitor;
synchronized (m) {
即只读发生一次,并且在不是线程安全的上下文中。
为什么主线程不能锁定新对象 - 在工作线程内重新分配。
这表示
- 一旦获得要锁定的对象,它就不会继续读取循环中的最新值以查看它是否可以锁定另一个对象。
- 即使在读取之前更改了引用,它也可以看到旧值,因为读取不是线程安全的。
推荐阅读
- influxdb - 通过 Influx HTTP API 写入数据返回 OK,但表为空
- r - 如何更改 ggplot2 geom_raster 中的插值/平滑
- c++ - Qt 驱动器下拉列表仅 USB 棒
- html - Groovy/Grails Hibernate 无法创建 bean 事务
- php - php 致命错误类Illumination\console\command not found
- excel - Excel:根据匹配复制整行在 Google Docs 中有效,但在 Excel 中无效
- c - C语言中没有指向单个字符的指针吗?
- php - 图像不会在文件夹路径中移动
- jco - 如何从 SAP UME 读取用户
- android - Android 8.0 EditText 文字颜色问题