多线程执行结果存入List的线程安全问题及解决方案

业务场景:在多线程环境下,将多个子线程的执行结果存入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的线程安全问题及解决方案