业务场景:在多线程环境下,将多个子线程的执行结果存入List时,经常出现List集合的长度小于子线程的执行数的情况。
1、错误示例:多个线程同时操作同一个List对象,List是线程不安全
package unitTest;
import org.assertj.core.util.Lists;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
// 线程个数
int N = 5;
// 实例化一个倒计数器,N指定计数个数
CountDownLatch countDownLatch = new CountDownLatch(N);
List<Thread> threadList = Lists.newArrayList();
List<String> list = Lists.newArrayList();
// List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < N; i++) {
Thread thread = new Thread(()-> {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
list.add(Thread.currentThread().getName());
countDownLatch.countDown();
});
thread.start();
threadList.add(thread);
}
// 阻塞,等待当计数减到0时,执行后面的代码
countDownLatch.await();
System.out.println("线程执行数量: "+threadList.size());
System.out.println("执行结果数量: "+list.size());
}
}
执行结果:
![](https://img2024.cnblogs.com/blog/2317593/202404/2317593-20240411152158316-1276467749.png)
![](https://img2024.cnblogs.com/blog/2317593/202404/2317593-20240411152203336-2063481736.png)
![](https://img2024.cnblogs.com/blog/2317593/202404/2317593-20240411152210391-247759297.png)
可以看见多次执行结果中,会存在执行结果集合数量小于线程执行总次数的情况
2、正确示例
1.使用Vector,是一个线程安全的List,但是它的线程安全实现方式是对所有操作都加上了synchronized关键字,这种方式严重影响效率.所以并不推荐使用Vector
2.使用 Collections.synchronizedList(List list),可以将add()等方法的时候是加synchronized关键字的,但是iterator()却没有加.所以在遍历使用的时候需要加上synchronized
“`java
package unitTest;
import org.assertj.core.util.Lists;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
// 线程个数
int N = 5;
// 实例化一个倒计数器,N指定计数个数
CountDownLatch countDownLatch = new CountDownLatch(N);
List<Thread> threadList = Lists.newArrayList();
// List<String> list = Lists.newArrayList();
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < N; i++) {
Thread thread = new Thread(()-> {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
list.add(Thread.currentThread().getName());
countDownLatch.countDown();
});
thread.start();
threadList.add(thread);
}
// 阻塞,等待当计数减到0时,执行后面的代码
countDownLatch.await();
System.out.println(“线程执行数量: “+threadList.size());
System.out.println(“执行结果数量: “+list.size());
}
}
执行结果:
![](https://img2024.cnblogs.com/blog/2317593/202404/2317593-20240411152224807-905636934.png)
可以发现,线程执行数量和执行结果数量相等,这就是线程不安全带来的后果
总结:在并发给List进行修改时,可以使用Vector或者Collections.synchronizedList(),不要直接使用ArrayList,在非并发情况下尽量使用ArrayList;
“`
未经允许不得转载:大白鲨游戏网 » 多线程执行结果存入List的线程安全问题及解决方案