【发布时间】:2017-06-15 03:47:53
【问题描述】:
假设我有两个这样运行的线程:
- 线程 A 在更新共享图像的像素时执行计算
- 线程 B 定期读取图像并将其复制到屏幕上
线程 A 快速执行工作,例如每秒 100 万次更新,所以我怀疑经常在锁/互斥/监视器上锁定和解锁是个坏主意。但是,如果没有锁并且无法建立从线程 A 到线程 B 的发生前关系,那么根据 Java 内存模型(JMM 规范),线程 B 根本不能保证看到 A 对图像的任何更新。
所以我认为最小的解决方案是线程 A 和 B 都在同一个共享锁上定期同步,但在同步块内实际上不执行任何工作 - 这就是使模式非标准和可疑的原因.用半真半伪代码来说明:
class ComputationCanvas extends java.awt.Canvas {
private Object lock = new Object();
private int[] pixels = new int[1000000];
public ComputationCanvas() {
new Thread(this::runThreadA).start();
new Thread(this::runThreadB).start();
}
private void runThreadA() {
while (true) {
for (1000 steps) {
update pixels directly
without synchornization
}
synchronized(lock) {} // Blank
}
}
private void runThreadB() {
while (true) {
Thread.sleep(100);
synchronized(lock) {} // Blank
this.repaint();
}
}
@Override
public void paint(Graphics g) {
g.drawImage(pixels, 0, 0);
}
}
这样添加空同步块是否正确实现了线程A向线程B传输数据的效果?还是有其他我无法想象的解决方案?
【问题讨论】:
-
为什么不能使用原子布尔值?什么都不同步仍然会使工作不同步
-
为什么不使用
volatile? -
在
pixels数组被A更新时,画布仍然可以读取数据 -
Graphics没有采用int[]数组的drawImage方法。因此,此示例中缺少一个基本步骤。此外,涉及三个线程,第三个线程是实际读取数组的事件调度线程(如果有接受int[]的drawImage方法)从不使用synchronized。由于repaint只会向队列发布某种事件(但有一些陷阱),因此您可以摆脱线程 B 和synchronized并简单地发布一个自定义事件来解决所有问题。 -
synchronized块已经超出了纯计算焦点。为什么任何其他也可以归结为一行代码的方法比这更糟糕?
标签: java multithreading synchronization java-memory-model happens-before