Pre*_*mar 8 java multithreading synchronization arraylist
多个线程同时向非同步ArrayList的对象添加元素会导致什么问题?
尝试使用具有多个线程的静态ArrayList运行一些实验,但找不到多少.
在这里,我期待在多线程环境中不同步ArrayList或类似对象的大部分副作用.
任何显示副作用的好例子都是可以理解的.谢谢.
下面是我的小实验,顺利进行,没有任何例外.
我也想知道它为什么不抛出任何东西ConcurrentModificationException
?
import java.util.ArrayList;
import java.util.List;
public class Experiment {
static List<Integer> list = new ArrayList<Integer>();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("A " + i);
new Thread(new Worker(list, "" + i)).start();
}
}
}
class Worker implements Runnable {
List<Integer> al;
String name;
public Worker(List<Integer> list, String name) {
this.al = list;
this.name = name;
}
@Override
public void run() {
while (true) {
int no = (int) (Math.random() * 10);
System.out.println("[thread " + name + "]Adding:" + no + "to Object id:" + System.identityHashCode(al));
al.add(no);
}
}
}
Run Code Online (Sandbox Code Playgroud)
调整列表大小以容纳更多元素时,通常会遇到问题。看执行ArrayList.add()
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Run Code Online (Sandbox Code Playgroud)
如果没有同步,则数组的大小将在调用ensureCapacityInternal
和实际插入元素之间改变。最终将导致ArrayIndexOutOfBoundsException
抛出。
这是产生此行为的代码
final ExecutorService exec = Executors.newFixedThreadPool(8);
final List<Integer> list = new ArrayList<>();
for (int i = 0; i < 8; i++) {
exec.execute(() -> {
Random r = new Random();
while (true) {
list.add(r.nextInt());
}
});
}
Run Code Online (Sandbox Code Playgroud)
通过将元素添加到多线程使用的未同步的 ArrayList 中,您可能会根据需要获得空值代替实际值。
发生这种情况是因为 ArrayList 类的以下代码。
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Run Code Online (Sandbox Code Playgroud)
ArrayList 类首先检查其当前容量,如果需要,则增加其容量(默认容量为 10,下一个增量为 (10*3)/2)并将默认类级别值放入新空间。
假设我们正在使用两个线程,并且两个线程同时添加一个元素,发现默认容量(10)已满,是时候增加它的容量了。首先,线程一个来使用默认值增加 ArrayList 的大小ensureCapacity 方法(10+(10*3/2)) 并将其元素放在下一个索引处(size=10+1=11),现在新的大小是 11。现在第二个线程来了,用默认值增加同一个 ArrayList 的大小再次使用 ensureCapacity 方法(10+(10*3/2)) 并将其元素放在下一个索引处 (size=11+1=12),现在新大小为 12。在这种情况下,您将在索引 10 处获得 null,即默认值。
这是上面的相同代码。
package com;
import java.util.ArrayList;
import java.util.List;
public class Test implements Runnable {
static List<Integer> ls = new ArrayList<Integer>();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Test());
Thread t2 = new Thread(new Test());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(ls.size());
for (int i = 0; i < ls.size(); ++i) {
System.out.println(i + " " + ls.get(i));
}
}
@Override
public void run() {
try {
for (int i = 0; i < 20; ++i) {
ls.add(i);
Thread.sleep(2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
39
0 0
1 0
2 1
3 1
4 2
5 2
6 3
7 3
8 4
9 4
10 null
11 5
12 6
13 6
14 7
15 7
16 8
17 9
18 9
19 10
20 10
21 11
22 11
23 12
24 12
25 13
26 13
27 14
28 14
29 15
30 15
31 16
32 16
33 17
34 17
35 18
36 18
37 19
38 19
Run Code Online (Sandbox Code Playgroud)
运行两三次后,有时在索引 10 和有时在 16 处获得空值。
正如上面 noscreenname 的回答中提到的,您可能会从此代码中获得 ArrayIndexOutOfBoundsException。如果您删除 Thread.sleep(2) 它将频繁生成。
请检查数组的总大小,该大小小于您的要求。根据代码,它应该是 40(20*2),但每次都会有所不同。
注意:您可能需要多次运行此代码才能生成一个或多个场景。
这是一个简单的示例:我从 10 个线程向列表中添加 1000 个项目。您希望最终有 10,000 个项目,但您可能不会。如果您运行多次,每次可能会得到不同的结果。
如果您想要 ConcurrentModificationException,您可以for (Integer i : list) { }
在创建任务的循环之后添加一个。
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Integer> list = new ArrayList<> ();
for (int i = 0; i < 10; i++) {
executor.submit(new ListAdder(list, 1000));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);
System.out.println(list.size());
}
private static class ListAdder implements Runnable {
private final List<Integer> list;
private final int iterations;
public ListAdder(List<Integer> list, int iterations) {
this.list = list;
this.iterations = iterations;
}
@Override
public void run() {
for (int i = 0; i < iterations; i++) {
list.add(0);
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
846 次 |
最近记录: |