多线程-多个子线程执行结果插入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());
    }
}

执行结果:
线程执行数量: 5
执行结果数量: 1

线程执行数量: 5
执行结果数量: 3

线程执行数量: 5
执行结果数量: 4
可以看见多次执行结果中,会存在执行结果数量小于线程执行数量的情况

2、正确示例
1.使用Vector,是一个线程安全的List,但是它的线程安全实现方式是对所有操作都加上了synchronized关键字,这种方式严重影响效率.所以并不推荐使用Vector
2.使用Collections.synchronizedList(List list),可以将add()等方法的时候是加synchronized关键字的,但是iterator()却没有加.所以在遍历使用的时候需要加上synchronized

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());
    }
}

执行结果:
线程执行数量: 5
执行结果数量: 5
可以发现,线程执行数量和执行结果数量相等,这就是线程不安全带来的后果
总结:在并发给List进行修改时,可以使用Vector或者Collections.synchronizedList(),不要直接使用ArrayList,在非并发情况下尽量使用ArrayList;

posted @ 2024-04-11 15:17  IT波少  阅读(186)  评论(2编辑  收藏  举报