大家都知道ArrayList并不是线程安全的,如果想要做到线程安全,我们可以使用 Collections.synchronizedList
, 但是使用 Collections.synchronizedList后是否真的就线程安全了?
1. Collections.synchronizedList 原理
工欲善其事必先利其器,我们先来看看Collections.synchronizedList 做了什么。
-
SynchronizedList.png
从源码来看,SynchronizedList 就是在 List的操作外包加了一层synchronize同步控制。
2. 加了 Collections.synchronizedList 后,为什么还需要使用 synchronize ?
首先我们看官方文档,可以发现, 当用户通过迭代器遍历返回列表时,必须手动同步:
It is imperative that the user manually synchronize on the returned list when traversing it via [Iterator
]
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
也就是说官方文档明确提出 对于 使用 Iterator遍历列表时,Collections.synchronizedList 可能发生 错误!
那除了直接使用 Iterator 要加 synchronize 保证线程安全,还有什么情况会间接使用到 Iterator吗? 那就是 for each增强for循环 ;
那我们先来写的demo验证下这种情况:
public class SynchronizedTest {
static List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<Integer>());
public static void main(String[] args) throws InterruptedException {
// 先存放1000个值让iterator有值可以遍历
for (int i = 0; i < 5; i++) {
synchronizedList.add(i);
}
Thread iteratorThread = new Thread(new IteratorRunnable(synchronizedList));
iteratorThread.start();
TimeUnit.SECONDS.sleep(1);
Thread modifyThread = new Thread(new ModifySynchronizeRunnable(synchronizedList));
modifyThread.start();
}
static class IteratorRunnable implements Runnable {
private List<Integer> list;
public IteratorRunnable(List<Integer> synchronizeList) {
this.list = synchronizeList;
}
@Override
public void run() {
while(true) {
for (Integer i : list) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + ",");
}
}
}
}
static class ModifySynchronizeRunnable implements Runnable {
private List<Integer> list;
public ModifySynchronizeRunnable(List<Integer> synchronizeList) {
this.list = synchronizeList;
}
@Override
public void run() {
while(true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(100);
System.out.println(" modify list container");
}
}
}
}
运行下看看结果,在使用Iteratior遍历的同时,异步修改List的结构,发现抛出了 ConcurrentModificationException
异常;
- ConcurrentModificationException.png
那怎么解决呢?官方文档说的很清楚,我们在 迭代器遍历返回列表时,增加手动同步处理,下面是IteratorRunnable 修改后 代码,仅仅是在外层加了 synchronized
static class IteratorRunnable implements Runnable {
private List<Integer> list;
public IteratorRunnable(List<Integer> synchronizeList) {
this.list = synchronizeList;
}
@Override
public void run() {
while(true) {
synchronized (list) {
for (Integer i : list) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + ",");
}
}
}
}
}
从运行结果来看,增加了synchronized 后,不会出现ConcurrentModificationException异常了;
- image.png
3. 探究下for each Java的实现
先写个简单的for each语句
-
for each.java.png
然后我们先来看看.class文件中for each
-
for each.class.png
看到这,我们就可以确定 ,其实JAVA中的增强for循环底层是通过iterator来实现的;
网友评论