多线程处理任务并合并数据
⼀、线程池创建四种⽅式
Java通过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 = wFixedThreadPool(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());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
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 = wFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
Runnable runnable = new Runnable(){
@Override
public void run() {
try {
Thread.sleep(3000);
java线程池创建的四种list.add(UUID.randomUUID().toString());
System.out.println("当前线程name : "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
countDownLatch.await();
System.out.println("submit总共cost 时间:" + (System.currentTimeMillis()-startTime)/1000 + "秒");
executorService.shutdown();
return list;
}
CopyOnWriteArrayList是Java并发包中提供的⼀个并发容器,它是个线程安全且读操作⽆锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是⼀种读写分离的并发策略,我们也可以称这种容器为"写时复制器"。
优点:读操作时性能很⾼,因为不需要任何同步措施,适⽤于读多写少的并发场景。
缺点:①.每次写操作都要copy原容器,频繁的GC,内存压⼒⼤。②.由于读写分离的策略,读到的数据很可能是旧数据。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论