这里有一个类,它有两个线程可以访问一个List。其中一个线程定期用更新后的副本替换列表,另一个线程将列表内容绘制到屏幕上。
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
期间需要多次访问列表,则必须将其存储在本地变量中。 - HolgerAtomic*
类还是volatile
字段,都取决于实际情况,但必须始终采取一些措施。 - biziclop