我对多线程还很陌生,想更好地理解它。我现在想知道如何在 Java 中使数组线程安全?这意味着我有多个线程访问和更改数组中的数据,并且我想防止一个线程更改数据,而另一个线程正在读取数据。
我的方法如下: 我有一个 Data 类,其中包含一个数组和两个方法 updateData(Integer[] data) 和 getUpdate() 用于访问和操作数组。
public class Data <T extends Comparable> {
private T[] data;
public T[] getUpdate() {
synchronized (this) {
return data.clone();
}
}
public void updateData(T[] data) {
synchronized (this) {
this.data = data.clone();
}
}
}
该数据对象现在在两个不同的线程中使用,主线程更新数据,swing EDT 线程读取数据并使用它。
public static void main(String[] args) {
Data<Integer> data = new Data<>();
data.updateData(new Integer[]{1,2,3,4,5,6,7,8,10});
SwingUtilities.invokeLater(() -> {
ColumnVisualizer visualizer = new ColumnVisualizer();
new Timer(100, e -> {
visualizer.setData(data.getUpdate());
}).start();
});
while(condition) {
data.updateData(createNewData());
}
}
据我了解,如果我仅通过 updateData() 和 getUpdate() 方法访问数组(我使用同步关键字使其成为线程安全的),那么数据本身就是线程安全的。因为如果一个线程调用 getUpdate() 方法,它会在 Data 实例上设置一个锁,并且当另一个线程想要调用 updateData() 时,它无法访问旧数组被新数组替换的部分,直到不再查看数据实例。
我是否理解正确?这是在 Java 中使数组线程安全的便捷方法吗?
您的理解是正确的。 Data 实例将被阻止,直到同步块在您拥有的任一方法(getUpdate/updateData)中完成为止。
不过,根据您的代码,我对您有一些观察,这可能对您有用。 假设在时间戳 time1,一个线程(我们称之为 th1)到来并调用 getUpdate 并获取数据的克隆。假设一毫秒后(或更早)另一个线程(我们称之为 I th2)从同一方法 getUpdate 获取克隆数据。 假设4毫秒后,进程th1使用updateData更新数据,1毫秒后,进程th2调用相同的方法。 让我们看看这两个进程发生了什么:在调用 th2 的 updateData 之后,数据表将包含 th2 放置在那里的任何内容,完全忽略 1 毫秒前放置在那里的内容。 这是你想要的吗 ?我并不是说这是错误的,我只是说如果 2 个进程几乎同时使用该表(相差 1 毫秒或更小),就会发生这种情况。 如果您想要的是在 th2 的末尾数据表包含 th1 和 th2 的信息,您可能需要不同的方法。 例如。锁定进程的表,或使用方法类 Data 添加/删除表中的条目,而不将整个表交给进程来更新它....