【发布时间】:2016-01-30 03:45:02
【问题描述】:
这里我有一个类,它有两个可以访问列表的线程。一个线程定期用更新的副本替换列表,另一个线程将列表的内容绘制到屏幕上。
public class ThreadSafePainter {
private List<String> dataList = new ArrayList<>();
/*
* starts a thread to periodically update the dataList
*/
public ThreadSafePainter() {
Thread thread = new Thread(() -> {
while (true) {
// replace out-dated list with the updated data
this.dataList = getUpdatedData();
// wait a few seconds before updating again
Thread.sleep(5000);
}
});
thread.start();
}
/*
* called 10 times/second from a separate paint thread
* Q: Does access to dataList need to be synchronized?
*/
public void onPaint(Graphics2D g) {
Point p = new Point(20, 20);
// iterate through the data and display it on-screen
for (String data : dataList) {
g.drawString(data, p.x, p.y);
p.translate(0, 20);
}
}
/*
* time consuming data retrieval
*/
private List<String> getUpdatedData() {
List<String> data = new ArrayList<>();
// retrieve external data and populate list
return data;
}
}
我的问题是,我需要同步对 dataList 的访问吗?我该怎么做呢?这行得通吗:
public ThreadSafePainter() {
...
synchronized (this) {
this.dataList = getUpdatedData();
}
...
}
public void onPaint(Graphics2D g) {
...
synchronized (this) {
for (String data : dataList)
...
}
}
【问题讨论】:
-
每次迭代都会重新绘制整个屏幕吗?
-
由于
getUpdatedData()每次都会创建一个新列表,因此您只需要一个安全的发布。在这种情况下,将字段dataList声明为volatile就足够了。如果列表引用在填充后被存储并且永远不会再次修改(因为下一次更新会创建一个新列表)并且读取器在每次处理时读取一次引用(如for(…: dataList)所做的那样),这很重要。如果它需要在一个paint期间多次访问该列表,则必须将其存储在一个局部变量中。 -
当两个或多个线程共享任何可变状态时,必须有某种机制来处理并发。无论是低级同步、高级并发类、
Atomic*类还是volatile字段,视实际情况而定,但必须始终到位。 -
谁调用 onPaint()?
-
我同意@Holger 的评估。此外,这可能超出了您的问题范围,但您似乎掩盖了 getUpdatedData() 的实现。您需要确保这也是线程安全的,这可能涉及与 volatile 同步或切换。
标签: java multithreading paint thread-synchronization