两个线程,同时操作一个List,会发生什么?
public static void main(String[] args) {
// 初始化一个list,放入5个元素
final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 5; i++) {
list.add(i);
}
// 线程一:通过Iterator遍历List
new Thread(new Runnable() {
@Override
public void run() {
for(int item : list) {
System.out.println("遍历元素:" + item);
try {
Thread.sleep(1000);// 由于程序跑的太快,这里sleep了1秒来调慢程序的运行速度
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// 线程二:remove一个元素
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);// 由于程序跑的太快,这里sleep了1秒来调慢程序的运行速度
} catch (InterruptedException e) {
e.printStackTrace();
}
list.remove(4);
System.out.println("list.remove(4)");
}
}).start();
}
1、ArrayList
ArrayList非线程安全,当线程一遍历的时候,线程二正在删除一个元素,线程一遍历不到该元素。
2、Vector
线程安全的Vector,内部很多方法,如add()、remove()、set()、get()、forEach()等都加上了synchronized来进行线程同步,代码如下:
list.forEach(item -> {
System.out.println("遍历元素:" + item);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
但如果用for(int item : list)来遍历,需手动同步,代码如下:
synchronized (list) {
for(int item : list) {
System.out.println("遍历元素:" + item);
// 由于程序跑的太快,这里sleep了1秒来调慢程序的运行速度
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、CopyOnWriteList
CopyOnWriteArrayList是java.util.concurrent包中的一个List的实现类。CopyOnWrite的意思是在写时拷贝,也就是如果需要对CopyOnWriteArrayList的内容进行改变,首先会拷贝一份新的List并且在新的List上进行修改,最后将原List的引用指向新的List。
使用CopyOnWriteArrayList可以线程安全地遍历,因为如果另外一个线程在遍历的时候修改List的话,实际上会拷贝出一个新的List上修改,而不影响当前正在被遍历的List。
4、工具类Collections使容器线程安全
//make thread-safe list
List MyStrList = Collections.synchronizedList(new ArrayList());
MyStrList.add("123");
MyStrList.add("abc");
//make thread-safe hashset
Set set=Collections.synchronizedSet(new HashSet());
set.add(123);
set.add(456);
//make thread-safe hashmap
Map map=Collections.synchronizedMap(new HashMap());
map.put(1, "thb");
map.put(12, "bill tang");