大家。为了测试Collections.synchronizedList()方法返回的ArrayList,我定义了一个静态类,如下所示。
static class ListWriter implements Runnable {
private List<Integer> list;
public ListWriter(List<Integer> list) {
this.list = list;
}
public void run() { // I didn't use synchronized(list) {} block here.
try {
for (int i = 0; i < 20; i++) {
list.add(i);
Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
以及如下的测试方法
private static void test1() throws Exception {
List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
Thread t1 = new Thread(new ListWriter(list));
Thread t2 = new Thread(new ListWriter(list));
t1.start();
t2.start();
t1.join();
t2.join();
for (int i = 0; i < list.size(); ++i) {
System.out.println(list.get(i));
}
}
结果通常如下: 0 0 1 1 2 2 3 3 4 4 五 五 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19
但有时它会是这样的:
0 0 1 1 2 3 2 3 4 五 4 五 6 7 6 8 7 9 8 10 9 11 10 12 11 13 12 14 13 15 14 15 16 17 16 17 18 19 18 19
这些结果是否表明该进程是线程安全的? 关键是我没有在写线程中使用synchronized块,迭代了Collection.synchronizedList()方法返回的同步ArrayList。那么你认为这个过程是线程安全的还是我还需要使用synchronized块?
答案 0 :(得分:1)
输出是非确定性的 - 它取决于底层操作系统的调度行为
有时你得到某个序列的事实只是一个巧合。
您的列表在创建时由Collections.synchronizedList
同步,因此您不需要任何其他同步。
对列表的线程安全性进行粗略测试将验证在两个线程完成后列表中包含的元素数量等于40。
如您所述,对于并发阅读,您需要按照the documentation
中的说明在返回的列表上进行同步答案 1 :(得分:0)
add
方法已同步,因此您无需为代码添加任何额外的同步。这些数字可能出现故障的原因是2个线程被Thread.sleep()
暂停,这不是很精确,因此有可能一个线程稍微超过另一个线程,产生第二个输出。
如果删除Thread.sleep()
,您将获得更多输出变体。
答案 2 :(得分:0)
答案 3 :(得分:0)
线程的目的是运行独立的任务。如果不是这种情况,他们必须协调他们的行动,这可能是一个开销。
令人惊讶的是,您既不能保证订单线程的运行,也不能保证它们的休眠时间。在第二个线程启动之前,您可以让一个线程运行完成。你可能让你的程序或机器崩溃,并且线程永远不会完成。
这些结果是否表明该进程是线程安全的?
这表示线程正在按原样独立运行。
关键是我没有在写入线程中使用synchronized块,这是迭代Collection.synchronizedList()方法返回的同步ArrayList所必需的
这不是问题,因为此时没有线程修改列表。
那么您认为该过程是线程安全的还是我还需要使用synchronized块?
没有。如果您期望订单一致,请不要使用线程。它会更简单,更快速,每次都会得到相同的结果。 ;)