精华内容
下载资源
问答
  • 我们在日常写代码的过程中,经常会使用多线程提高效率,我们在使用多线程过程中难免会出现往List集合修改数据。 下面我们来尝试一下往ArrayList 添加数据: public static void main(String[] args) { List<...

    我们在日常写代码的过程中,经常会使用多线程提高效率,我们在使用多线程过程中难免会出现往List集合修改数据。
    下面我们来尝试一下往ArrayList 添加数据:

    public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            for (int i = 10000000; i >= 1; i--) {
                list.add(0);
            }
            System.out.println("源集合数量:"+list.size());
            List<Integer> newList = new ArrayList<>();
            long start = System.currentTimeMillis();
    
            ExecutorService executor = Executors.newFixedThreadPool(100);
            for (Integer integer : list) {
                executor.submit(()->{
                    newList.add(integer+1);
                });
            }
            executor.shutdown();
            try {
                executor.awaitTermination(6, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println("时间:"+(end-start)+"ms");
    
            System.out.println("新集合数量:"+newList.size());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    我们使用线程池给 ArrayList 添加一千万个元素。来看下结果:

    在这里插入图片描述

    会发现新List’的数据会少于一千万,这是为什么呢?

    因为 ArrayList 不是线程安全的,在高并发情况下对list进行数据添加会出现数据丢失的情况。
    并且一个线程在遍历List,另一个线程修改List,会报ConcurrentModificationException(并发修改异常)错误

    那么如果我们确实需要 并发对数据进行操作,并且对结果进行收集处理,应该怎么做呢?
    一,使用Vector

    在这里插入图片描述


    从源码介绍里面我们可以看出 Viector是线程安全的,但后面也说明了,如果对线程安全没有要求,建议使用ArrayList,因为ArrayList单分效率更高。
    从源码里面可以看到:

    /**
         * Sets the size of this vector. If the new size is greater than the
         * current size, new {@code null} items are added to the end of
         * the vector. If the new size is less than the current size, all
         * components at index {@code newSize} and greater are discarded.
         *
         * @param  newSize   the new size of this vector
         * @throws ArrayIndexOutOfBoundsException if the new size is negative
         */
        public synchronized void setSize(int newSize) {
            modCount++;
            if (newSize > elementCount) {
                ensureCapacityHelper(newSize);
            } else {
                for (int i = newSize ; i < elementCount ; i++) {
                    elementData[i] = null;
                }
            }
            elementCount = newSize;
        }
    
        /**
         * Returns the current capacity of this vector.
         *
         * @return  the current capacity (the length of its internal
         *          data array, kept in the field {@code elementData}
         *          of this vector)
         */
        public synchronized int capacity() {
            return elementData.length;
        }
    
        /**
         * Returns the number of components in this vector.
         *
         * @return  the number of components in this vector
         */
        public synchronized int size() {
            return elementCount;
        }
    
        /**
         * Tests if this vector has no components.
         *
         * @return  {@code true} if and only if this vector has
         *          no components, that is, its size is zero;
         *          {@code false} otherwise.
         */
        public synchronized boolean isEmpty() {
            return elementCount == 0;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    Vector里面的操作方法,都加上了synchronized 关键字。下面来使用Vector走一遍代码:

    public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            for (int i = 10000000; i >= 1; i--) {
                list.add(0);
            }
            System.out.println("源集合数量:"+list.size());
            List<Integer> newVector = new Vector<>();
            long start = System.currentTimeMillis();
    
            ExecutorService executor = Executors.newFixedThreadPool(100);
            for (Integer integer : list) {
                executor.submit(()->{
                    newVector.add(integer+1);
                });
            }
            executor.shutdown();
            try {
                executor.awaitTermination(6, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println("时间:"+(end-start)+"ms");
    
            System.out.println("newVector数量:"+newVector.size());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    看下结果:

    在这里插入图片描述


    我们可以发现现在,新Vector里面的数量正好是一千万个。但是时间上要长于ArrayList。

    二、使用Collections.synchronizedList()进行包装

    public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            for (int i = 10000000; i >= 1; i--) {
                list.add(0);
            }
            System.out.println("源集合数量:"+list.size());
            /**
             * Collections.synchronizedList()包装
             */
            List<Integer> newCollList = Collections.synchronizedList(new ArrayList<>());
            long start = System.currentTimeMillis();
    
            ExecutorService executor = Executors.newFixedThreadPool(100);
            for (Integer integer : list) {
                executor.submit(()->{
                    newCollList.add(integer+1);
                });
            }
            executor.shutdown();
            try {
                executor.awaitTermination(6, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println("时间:"+(end-start)+"ms");
    
            System.out.println("newCollList新集合数量:"+newCollList.size());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    结果:

    在这里插入图片描述


    我们可以发现也是一千万条。时间上和Vector差距不大,因给给ArrayList进行了包装以后等于是给ArrayList里面所有的方法都加上了 synchronized,和Vector实现效果差不多。

    总结:在并发给List进行修改时,可以使用Vector或者Collections.synchronizedList(),不要直接使用ArrayList,在非并发情况下尽量使用ArrayList;

    版权声明:本文为flycp原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    本文链接:使用多线程往List添加数据_cpown的博客-CSDN博客_多线程向list添加元素

    展开全文
  • List数据分段以后再使用多线程执行,提高效率 大致业务场景,需要后台刷新产品详情缓存,for循环跑需要1个多小时才能执行完毕。 最后使用多线程处理,伪代码如下 //查询所有商品list ,商品较多几千只,可能一下起...

    List数据分段以后再使用多线程执行,提高效率

    大致业务场景,需要后台刷新产品详情缓存,for循环跑需要1个多小时才能执行完毕。
    最后使用多线程处理,伪代码如下

    //查询所有商品list ,商品较多几千只,不可能一下起几千线程处理。所以先将list分割
    // PAGE_SIZE 可在配置中心设定,动态调整list分割数量
    List<String> idList;
    	int currentPage = 1;
    		int pageSize = Integer.valueOf(PAGE_SIZE);
    		int totalPage = idList.size() % pageSize>0?(idList.size() / pageSize)+1:idList.size() / pageSize;
    		while (currentPage <= totalPage) {
    			//把list分段
    			List<String> currentList = pageBySubList(idList, currentPage, pageSize);
    			//具体多线程处理逻辑
    			executeRefreshDetail(currentList);
    			currentPage += 1;
    		}
    
     /**
    	 * 这里是每一段list
    	 * 然后使用多线程处理,利用CountDownLatch 机制,保证一批次全部处理完成后再进行下一段。防止过多任务,压垮下游服务
    	 */
    private void executeRefreshDetail(List<String> idList) {
    		if (CollectionUtils.isEmpty(idList)) {
    			return;
    		}
    		try {
    			CountDownLatch countDownLatch = new CountDownLatch(idList.size());
    			for (int i = 0; i < idList.size(); i++) {
    				String id= idList.get(i);
    				ThreadTaskPool.getThreadPool().execute(() -> {
    					//这里拿到执行后返回的对象
    					// 此前考虑使用Future,但get方法会阻塞线程,不能实现多线程调用
    					BaseResult  baseResult = pofDetailsService.refreshDetailResult(id);
    					log.info("{}执行结果{}", id, baseResult.getStatus());
    					countDownLatch.countDown();
    				});
    			}
    			countDownLatch.await(10, TimeUnit.MINUTES);
    		} catch (Exception e) {
    			log.error("任务执行失败,原因:{}", e);
    		}
    	}
    
    /**
    	 * 利用subList方法进行分页
    	 * 
    	 * @param list
    	 *            分页数据
    	 * @param pageSize
    	 *            页面大小
    	 * @param currentPage
    	 *            当前页面
    	 */
    	protected static List<String> pageBySubList(List<String> list, int currentPage, int pageSize) {
    		int totalcount = list.size();
    		int pagecount = 0;
    		List<String> subList;
    		int m = totalcount % pageSize;
    		if (m > 0) {
    			pagecount = totalcount / pageSize + 1;
    		} else {
    			pagecount = totalcount / pageSize;
    		}
    		if (m == 0) {
    			subList = list.subList((currentPage - 1) * pageSize, pageSize * (currentPage));
    		} else {
    			if (currentPage == pagecount) {
    				subList = list.subList((currentPage - 1) * pageSize, totalcount);
    			} else {
    				subList = list.subList((currentPage - 1) * pageSize, pageSize * (currentPage));
    			}
    		}
    		return subList;
    	}
    

    下图参考自:https://blog.csdn.net/u011726984/article/details/79320004
    在这里插入图片描述
    后续优化会尝试使用不同方式。但目前情况不需要获取结果归集。如有问题还请大家指出,谢谢。

    展开全文
  • 多并发下多线程遍历list问题

    千次阅读 2021-02-26 18:50:48
    今天做导出 查出的数据有3万条 还要遍历做一些处理 结果就超时想到多线程遍历list 去网上查找 找到这块代码:public String list2Str(List list, final int nThreads) throws Exception {if (list == null || list....

    今天做导出 查出的数据有3万条 还要遍历做一些处理 结果就超时

    想到多线程遍历list 去网上查找 找到这块代码:

    public String list2Str(List list, final int nThreads) throws Exception {

    if (list == null || list.isEmpty()) {

    return null;

    }

    StringBuffer ret = new StringBuffer();

    int size = list.size();

    ExecutorService executorService = Executors.newFixedThreadPool(nThreads);

    List> futures = new ArrayList>(nThreads);

    for (int i = 0; i < nThreads; i++) {

    final List subList = list.subList(size / nThreads * i, size / nThreads * (i + 1));

    Callable task = new Callable() {

    @Override

    public String call() throws Exception {

    StringBuffer sb = new StringBuffer();

    for (String str : subList) {

    sb.append(str);

    }

    return sb.toString();

    }

    };

    futures.add(executorService.submit(task));

    }

    for (Future future : futures) {

    ret.append(future.get());

    }

    executorService.shutdown();

    return ret.toString();

    }

    }

    我调用时开了5个线程 我想问 每次导出就开启5个线程 如果多个人同时导出 比如10000个人同时导出 那岂不是就开启了50000个线程了!!!

    没问题吗???

    展开全文
  • Java多线程并发中支持并发的list对象

    千次阅读 2021-03-08 16:59:50
    Java多线程并发编程中并发容器第二篇之List的并发类讲解概述本文我们将详细讲解list对应的并发容器以及用代码来测试ArrayList、vector以及CopyOnWriteArrayList在100个线程向list中添加1000个数据后的比较本文是...

    Java多线程并发编程中并发容器第二篇之List的并发类讲解

    概述

    本文我们将详细讲解list对应的并发容器以及用代码来测试ArrayList、vector以及CopyOnWriteArrayList在100个线程向list中添加1000个数据后的比较

    本文是《凯哥分享Java并发编程之J.U.C包讲解》系列教程中的第六篇。如果想系统学习,凯哥(kaigejava)建议从第一篇开始看。

    从本篇开始,我们就来讲解讲解Java的并发容器。大致思路:先介绍什么是并发容器。然后讲解list相关的、map相关的以及队列相关的。这个系列会有好几篇文章。大家最好跟着一篇一篇学。

    正文开始

    并发容器分类讲解

    CopyOneWriteArrayList

    Copy-One-Write:即写入时候复制。

    我们知道在原来List子类中vactor是同步容器线程安全的。这个CopyOneWriteArrayList可以理解为是他的并发替代品。

    其底层数据结构也是数值。和ArrayList的不同之处就在于:在list对象中新增或者是删除元素的时候会把原来的集合copy一份,增删操作是在新的对象中操作的。操作完成之后,会将新的数组替换原来的数组。

    我们来看看CopyOnWriteArrayList源码中的add方法:

    55ae50d1f4f689ee3b66c027e940064d.png

    编辑

    我们来看看setArray方法:

    5a82add5e6086f7bc0f7ff25c6e09ff6.png

    发现了吗?变量使用的是transient和volatile两个关键之来修饰的。

    在之前文章中,我们知道了volatile关键字是内存可见性。那么transient关键字是干嘛的呢?我们来看下百科解释:

    d3fb381a9a02098f7fe93635292f2fb7.png

    关键的一句话:用transient关键字修饰的成员变量不用参与序列化过程。

    添加注释后的源码:

    b4a65d45ea0da2ca5333adcac5bc92af.png

    public boolean add(E e) {

    final ReentrantLock lock = this.lock;

    //获取到锁

    lock.lock();

    try {

    //获取到原集合

    Object[] elements = getArray();

    int len = elements.length;

    //将原集合copy一份到新的集合中。并设置新的集合的长度为原集合长度+1

    Object[] newElements = Arrays.copyOf(elements, len + 1);

    //将需要新增的元数添加到新的素组中

    newElements[len] = e;

    //将新数组替换原来数据。 使用transient和volatitle关键字修饰的

    setArray(newElements);

    return true;

    } finally {

    lock.unlock();

    }

    代码很简单,大致流程如下:

    先从ReentrantLock中获取到锁(这样在多线程下可以防止其他线程来修改容器list里面内容了);

    通过arrays.copyOf方法copy出一份原有数组长度+1;

    将要添加的元素赋值给copy出来的数组;

    使用setArray方法将新得数组替换原有素组。

    因为都是List集合。我们就拿ArrayList、vector以及CopyOnWriteArrayList进行比较:

    ArrayList、vector以及CopyOnWriteArrayList比较

    业务场景描述:

    启动100个线程,向对象中添加1000个数据。查看各自运行结果耗时及插入数据总数。代码在文章最后凯哥会贴出来。

    先用线程非安全的arrayList执行效果:

    56d0cacb0fa159c807b4ee9c848294c7.png

    执行arryList时间为 : 112毫秒!

    List.size() : 93266

    我们发现list的长度不对。正确的应该是100*1000.从结果来看,arrayList丢数据了。

    使用Vector执行后的效果:

    3b3b054193e383edee346efb5a65d51a.png

    执行vector时间为 : 98毫秒!

    List.size() : 100000

    执行的总数对,说下同步锁没有丢数据。

    在来看看copyOnWriteArrayList执行效果:

    48c71f5ab683f81840e54dbbe4e7ad3d.png

    执行copyOnWriteArrayList时间为 : 5951毫秒!

    运行后数据比较:

    20dad903ef2c0631d82f02656c1b8e19.png

    从上面表格中我们可以看出非安全线程的容器会丢数据。使用copyOneWriteArrayList耗时很长。那是因为每次运行都要copyof一份。

    总结

    copyArrayList(这里凯哥就简写了):是读写分离的。在写的时候会复制一个新的数组来完成插入和修改或者删除操作之后,再将新的数组给array.读取的时候直接读取最新的数据。

    因为在写的时候需要向主内存申请控件,导致写操作的时候,效率非常低的(虽然在操作时候比较慢得,但是在删除或者修改数组的头和尾的时候还是很快的。因为其数据结构决定查找头和尾快,而且执行不需要同步锁)

    从上面表中,可以看出copyArrayList虽然保证了线程的安全性,但是写操作效率太low了。但是相比Vector来说,在并发安全方面的性能要比vector好;

    CopyArrayList和Vector相比改进的地方:

    Vector是在新增、删除、修改以及查询的时候都使用了Synchronized关键字来保证同步的。但是每个方法在执行的时候,都需要获取到锁,在获取锁等待的过程中性能就会大大的降低的。

    CopyArrayList的改进:只是在新增和删除的方法上使用了ReentrantLock锁进行(这里凯哥就不截图源码了,自己可以看看源码)。在读的时候不加锁的。所以在读的方面性能要不vector性能要好。

    所以,CopyArrayList支持读多写少的并发情况

    CopyOnWriteArrayList的使用场景:

    由于读操作不加锁,增删改三个操作加锁的,因此适用于读多写少的场景,

    局限性:因为读的时候不加锁的,读的效率和普通的arrayList是一样的。但是请看读操作:

    961496291f68e5dd8b6f018fe7a8c263.png

    a25ce40e1cf60c53a33c4e3097c9a791.png

    在get的时候array使用的是volatile修饰的。是内存可见的。所以可以说copyArrayList在读的时候不会出现arrayList读取到脏数据的问题。

    Get(i)方法比较如下:

    325fddf3ebcb0df7d25a74b0d3594dba.png

    附件:arrayList、vector、copyOnwriteArrayList比较的代码:

    public static voidmain(String[] args) {//使用线程不安全的arrayList// List arryList = new ArrayList<>();//使用vector// List arryList = new Vector<>();

    //使用copyOnWriteArrayListList arryList = newCopyOnWriteArrayList<>();Random random = newRandom();Thread [] threadArr = newThread[100];CountDownLatch latch = newCountDownLatch(threadArr.length);Long beginTime = System.currentTimeMillis();for(inti = 0;i

    展开全文
  • import java.util.ArrayList;...public class Test7 {public static void main(String[] args){List list = new ArrayList();for(int i=0;i<10;i++){list.add(i+"");}List sbs = new ArrayLis...
  • import java.util....import java.util.List;import static java.lang.Thread.sleep;public class Test3 {public static void main(String[] args) throws Exception {long start = System.currentTimeMillis...
  • 多线程下的 list安全 or 安全? 安全!通常我们说的线程安全是指针对某个数据结构的所有操作都是线程安全,在这种定义下,Python 常用的数据结构 list,dict,str等都是线程安全的尽管多线程下的li...
  • 开发过程中经常遇到如下需求,拿到一个list数据,需要遍历它然后请求第三方接口,然后根据返回结果做一些事 常规写法 public static void main(String[] args) { List<User> list = getList(); test1(list);...
  • 创建多线程 public static void createThreadPoolBatch() { List<String> lists = formatList(); ExecutorService executorService = new ThreadPoolComponent().getFullIOExecutorService(5, 60L, ...
  • java中使用多线程不能明显提高程序效率的一些原因.使用多个线程来处理多任务的时候,效率肯定是有提高的.但是必须要慎用,否则容易出现问题.1.多线程主要是为了充分利用多核cpu,大内存这些资源.如果你的硬件跟上,...
  • public ExecupteHp (List list){this.list =list ; } @Overridepublic voidrun() {if(null!=list){for(SidCustPersonContactInfo scpc : list){ System.out.print("更新会员联系方式" +scpc.getFirstName() + "---"+...
  • 场景:大数据List集合,... 将对比操作在多线程中实现 public static void main(String[] args) throws Exception { // 开始时间 long start = System.currentTimeMillis(); List<String> list = new Arr.
  • List集合多线程并发前言一、List集合使用模拟并发测试1.1 单线程环境下1.2 多线程环境下二、解决方案2.1 使用Vector类2.1 使用Collections.synchronizedList2.3 使用并发容器CopyOnWriteArrayList总结 前言 在日常...
  • }//如果数量大于2000,分割ArrayList进行多线程; else{for (int i = 0; i (); i++) {int num = i / 2000;if (i % 2000 == 0) { //i = 0, 2000, 4000... arrayListArrayList.add(new ArrayList(2000)); }if ...
  • 多线程数量的问题,一般情况下,多线程数量要等于机器CPU核数-1。 二、实例 1、解决问题:如何让n个线程顺序遍历含有n个元素的List集合 import java.util.ArrayList; import java.util.List; import org.apache....
  • 多线程list.add()丢数据

    2021-03-01 06:41:24
    原因:该接口在之前一次优化时加了多线程,但是数据汇总用的仍是ArrayList,ArrayList线程安全,当一条线程执行list.add()的时候会出现被另一条线程的值覆盖,比如线程A对list进行add,携带的size是n,同时线程B也...
  • 目录一、ArrayList的安全二、Vector三、SynchronizedList四、CopyOnWriteArrayList 一、ArrayList的安全 public class ArrayListDemo { public static void main(String[] args) { List<String> list =...
  • redis list 实现消息队列 多线程消费
  • 遍历List的多种方式在讲如何线程安全地遍历List之前,先看看通常我们遍历一个List会采用哪些方式。方式一:for(int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}方式二:Iterator iterator ...
  • Springboot自定义线程池,多线程遍历list及有返回值 自定义连接池 /** * @description: 某功能公共线程池 * @author: zjq * @create: 2021-01-06 15:35 **/ @Configuration public class ...
  • 题主的目的:一个线程插入大批量的数据,和线程同时插入大批量的数据,在时间上是否有差距,所以在线程里用正常的orm方式操作数据库;他想用Spring-Data-Jpa这种方式去插入数据,简化操作。看着像SpringBoot应用...
  • 针对此问题,查阅了网上很多资料,好多都使用多线程来处理。跟着好多的博客进行处理,要么是线程安全问题,要么根本速度就提高了。我针对我项目中的使用场景,结合资料进行了修改,特提交此文,为有共同需求的小...
  • 多线程处理List并将结果合并

    千次阅读 2021-02-21 20:07:10
    打算使用多线程解决。一开始打算使用@Async 异步多线程操作。后面发现在合并操作结果的时候没有思路。 查找相关资料以后,使用了Callable和Future解决了问题。 2.相关知识点: Callable接口定义了方法public T ...
  • 作者:雪山上的蒲公英www.cnblogs.com/zjfjava/p/10217720.htmlArrayList 不是线程安全的,这点很多人都知道,但是线程不安全的原因及表现,怎么在...
  • 前言碎语最近可能要批量处理一些数据,准备使用多线程来助力,回顾了下多线程的一些开发技巧,下面是多线程并行处理List的一个小例子代码看关键/*** @author kl by 2016/6/25* @boke www.kailing.pub*/public class ...
  • 1.错误示例(线程同时操作同一个List对象,List线程不安全) import com.alibaba.fastjson.JSONObject; import java.util.*; import java.util.concurrent.CountDownLatch; public class Main { public ...
  • 文章借鉴自:...前提:日常开发,有很多场景会使用多线程,比如,我们解析Excel,如果解析出一个3万条数据的Excel表格,需要两部:1.我们需要先异步解析出所有的数据,前面写过了如何...
  • 这段代码中,我们创建了两个线程,同时对ArrayList添加10000个元素,如果我们运行这段代码,我们肯定期望它返回的是20000。可是我在JDK1.8环境中运行这段代码,次验证,会出现两种结果: import java.util.*; ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 521,298
精华内容 208,519
关键字:

多线程不能使用list