mic*_*alg 3 java multithreading arraylist volatile
让我们假设:
查看 ArrayList 源代码,我们可以看到 size 和 elementData 字段不是可变的:
transient Object[] elementData; // non-private to simplify nested class access
private int size;
Run Code Online (Sandbox Code Playgroud)
另外,让我们看看 add 方法:
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value),
* which helps when add(E) is called in a C1-compiled loop.
*/
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
Run Code Online (Sandbox Code Playgroud)
这样的事情会发生吗?
elementData 会发生类似的情况吗?
TL;DR:您描述的问题在正确同步的情况下是不可能的,因为同步确保了操作的原子性和可见性。
JVM 执行 Java 代码的方式相当复杂。可以自由地重新排序与 Java 代码中的表达式和语句相对应的指令,以便更有效地执行它们,前提是您无法判断线程已对其操作重新排序。
从本质上讲,这就像一个老板说“我不在乎你如何完成工作,只要在 [某个时间] 之前完成 [工作]”。
这样做的困难在于,虽然它说你不能看到一个线程内的重新排序,但它并不是说不同的线程不能看到彼此以不同的顺序做事。
这是令人头疼的事情。简化的概念是happens-before的想法。您可以在两个线程中执行某些操作,以确保一个线程完成的操作在另一个线程尝试使用它们的结果时似乎已经发生。从字面上看,一个线程中的事情在另一个线程中“发生过”。(继续工作类比,这就像必须将您完成的工作交给同事才能让他们完成他们的工作:他们可以拿走您已完成的工作并完成他们的工作,而不管您如何完成它)。
有许多众所周知的事情可以创建先发生关系。就这个问题而言,相关的有:
因此, volatile 和 synchronized 都是创建happens-before的两种方式,这是保证一个线程完成的[某事]被另一个线程看到所必需的。
但是两者有一个区别:
在添加到 an 的情况下,ArrayList需要原子性,因为您要做的不止一件事:增加大小并分配新的数组元素。
将变量设置为 volatile 对正确性没有任何意义,但它会使代码在模态情况下变慢,这就是 ArrayList只能从单个线程访问。
因此,如果您的代码正确同步 - 也就是说,对列表的所有访问都在同一事物上同步,例如在列表本身上 - 您描述的情况不会发生,因为同步的原子性和可见性属性。
| 归档时间: |
|
| 查看次数: |
129 次 |
| 最近记录: |