多线程处理任务并合并数据

一、线程池创建方式

顶层接口:Executor executor.(Runnable r)
子接口:ExecutorService submit(Runnable task)
抽象类:AbstractExecutorService
实现类:ThreadPoolExecutor

1.使用构造方法创建
public  ThreadPoolExecutor(
int corePoolSize,      //核心线程池大小【如果有核心空闲线程,任务会被执行,执行结束不销毁线程】
int maximumPoolSize,    //线程池能创建的最大线程数量 【核心线程池和缓存队列都满了,新来的任务会创建新的线程,最大不超过max】
long keepAliveTime   //非核心线程池能够空闲的最大时长【非核心线程能够空闲的最长时间,超过时间 线程终止】
TimeUnit unit,    //时间单位
BlockingQueue<Runnable> workQueue //缓存队列,存放等来被执行的任务
ThreadFactory  threadFactory  //线程工厂,用来创建线程
RejectedExecutionHandler handler  //拒绝处理策略 
)
2.使用Executors创建
newCachedThreadPool //创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool  //创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool //创建一个定时线程池,支持定时及周期性任务执行。
newSingleThreadExecutor //创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
二、有返回值的多线程

ExecutorService接口继承自Executor,Executor中的execute方法无返回值,ExecutorService接口中的方法有返回值。
在这里插入图片描述
在这里插入图片描述

三、计数器的使用

CountDownLatch也是juc包中的一个类,类似倒计时计数器,创建对象时通过构造方法设置初始值,调用CountDownLatch对象的await()方法则处于等待状态,调用countDown()方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。

有了计数器就可以暂时将主线程阻塞,等异步的多线程全部执行完毕并返回结果后,再继续执行主线程。

四、线程安全问题

有了上面线程池跟计数器的基础,现在可以动手写一个多线程处理任务并合并数据的demo了。

大致思路就是:创建一个定长的线程池,长度为10,计数器初始值也设置为10。每执行一次,将计数器减一,并且将执行结果添加到list集合中,最终多线程全部执行完毕后,计数器停止等待 主线程继续往下执行,返回list。


public static List<String> getExecutorService() throws InterruptedException{
		System.out.println("开始执行多线程...");
		long startTime = System.currentTimeMillis();
		List<String> list = new ArrayList<>();//存放返回结果
		CountDownLatch countDownLatch = new CountDownLatch(10);
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			Runnable runnable = new Runnable(){
 
				@Override
				public void run() {
					 try {
						Thread.sleep(3000);
                        list.add(UUID.randomUUID().toString());
                        System.out.println("当前线程name : "+Thread.currentThread().getName());
                        countDownLatch.countDown();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			};
			executorService.execute(runnable);
		}
		countDownLatch.await();
		System.out.println("submit总共cost 时间:" + (System.currentTimeMillis()-startTime)/1000 + "秒");
		executorService.shutdown();
		return list;
	}

执行结果如下:
在这里插入图片描述
十个线程全部工作,但是返回值中却有null值,跟想要的结果有点出入,为啥呢?

原因在于:ArrayList是非线程安全的。ArrayList的add方法中有size++,不是原子操作,所以线程不安全。
在这里插入图片描述

五、CopyOnWriteArrayList的用法

part4中提到的问题 解决方案很简单,将ArrayList换成CopyOnWriteArrayList即可。

public static List<String> getExecutorService() throws InterruptedException{
		System.out.println("开始执行多线程...");
		long startTime = System.currentTimeMillis();
		List<String> list = new CopyOnWriteArrayList<>();//存放返回结果
		CountDownLatch countDownLatch = new CountDownLatch(10);
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 10; i++) {
			Runnable runnable = new Runnable(){
 
				@Override
				public void run() {
					 try {
						Thread.sleep(3000);
                        list.add(UUID.randomUUID().toString());
                        System.out.println("当前线程name : "+Thread.currentThread().getName());
                        countDownLatch.countDown();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			};
			executorService.execute(runnable);
		}
		countDownLatch.await();
		System.out.println("submit总共cost 时间:" + (System.currentTimeMillis()-startTime)/1000 + "秒");
		executorService.shutdown();
		return list;
}

执行结果如下:
在这里插入图片描述
CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器"。

优点:读操作时性能很高,因为不需要任何同步措施,适用于读多写少的并发场景。

缺点:①.每次写操作都要copy原容器,频繁的GC,内存压力大。②.由于读写分离的策略,读到的数据很可能是旧数据。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值