日常开发中,我们常常需要处理大批量的数据,为了增加处理速度,提高程序执行效率,优先考虑到的是用多线程处理,这就涉及到要通过多线程去处理一件事或者一批数据,然后统计这些线程的处理结果。
除了使用线程的join()方法外,还可以使用CountDownLatch和CyclicBarrier这两个辅助工具,下面直接用代码说事,纯属个人使用心得,如有错误,欢迎指正。
一、使用线程的join()方法实现结果统计
Java代码:
1 /** 2 * @author goode 3 * @date 2017-11-16 17:55:00 4 */ 5 public class MyThread extends Thread{ 6 7 private Vector<String> vector; 8 9 private Integer time; 10 11 public MyThread(Vector<String> vector, Integer time) { 12 this.vector = vector; 13 this.time = time; 14 } 15 16 public void run() { 17 System.out.println("==子线程" + Thread.currentThread().getName() + "开始执行..."); 18 try { 19 vector.add(Thread.currentThread().getName()); 20 Thread.sleep(time); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 System.out.println("==子线程" + Thread.currentThread().getName() + "执行完毕"); 25 } 26 }
1 public static void main(String[] args) { 2 long start = System.currentTimeMillis(); 3 List<Thread> list = Lists.newArrayList(); 4 Vector<String> vector = new Vector<>(); 5 for (int i = 1; i <= 5; i++) { 6 Thread thread = new MyThread(vector, i * 1000); 7 thread.start(); 8 list.add(thread); 9 } 10 try { 11 for (Thread thread : list) { 12 thread.join();//阻塞主线程 13 } 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 long end = System.currentTimeMillis(); 18 System.out.println("====子线程执行时长:" + (end - start)); 19 System.out.println("====多线程处理结果集长度:" + vector.size()); 20 }
程序输出结果:
==子线程Thread-3开始执行... ==子线程Thread-1开始执行... ==子线程Thread-2开始执行... ==子线程Thread-0开始执行... ==子线程Thread-4开始执行... ==子线程Thread-0执行完毕 ==子线程Thread-1执行完毕 ==子线程Thread-2执行完毕 ==子线程Thread-3执行完毕 ==子线程Thread-4执行完毕 ====子线程执行时长:5086 ====多线程处理结果集长度:5
二、使用CountDownLatch实现多线程处理数据
Java代码:
1 /** 2 * @author goode 3 * @date 2017-09-30 14:10:00 4 */ 5 public class DoSomeThings implements Runnable { 6 7 private CountDownLatch latch; 8 9 private Integer time; 10 11 private Vector<String> vector; 12 13 public DoSomeThings(Vector<String> vector, CountDownLatch latch, int time) { 14 this.latch = latch; 15 this.time = time; 16 this.vector = vector; 17 } 18 19 20 @Override 21 public void run() { 22 System.out.println("====子线程" + Thread.currentThread().getName() + "开始执行..."); 23 try { 24 Thread.sleep(time); 25 vector.add(Thread.currentThread().getName()); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } finally { 29 latch.countDown(); 30 } 31 System.out.println("====子线程" + Thread.currentThread().getName() + "执行完成,剩余执行中线程数据量:" + latch.getCount()); 32 } 33 }
1 public static void main(String[] args) { 2 System.out.println("====主线程" + Thread.currentThread().getName() + "开始...."); 3 CountDownLatch latch = new CountDownLatch(5); 4 ExecutorService es = Executors.newFixedThreadPool(5); 5 Vector<String> list = new Vector<>(); 6 for (int i = 1; i <= 5; i++) { 7 es.execute(new DoSomeThings(list, latch, i*1000)); 8 } 9 try { 10 latch.await(); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 System.out.println("====主线程" + Thread.currentThread().getName() + "结束,获取处理后的数据长度=" + list.size()); 15 es.shutdown(); 16 }
程序输出结果:
====主线程main开始.... ====子线程pool-1-thread-1开始执行... ====子线程pool-1-thread-2开始执行... ====子线程pool-1-thread-3开始执行... ====子线程pool-1-thread-4开始执行... ====子线程pool-1-thread-5开始执行... ====子线程pool-1-thread-1执行完成,剩余执行中线程数据量:4 ====子线程pool-1-thread-2执行完成,剩余执行中线程数据量:3 ====子线程pool-1-thread-3执行完成,剩余执行中线程数据量:2 ====子线程pool-1-thread-4执行完成,剩余执行中线程数据量:1 ====子线程pool-1-thread-5执行完成,剩余执行中线程数据量:0 ====主线程main结束,获取处理后的数据长度=5
三、使用CyclicBarrier实现多线程处理数据
Java代码:
1 /** 2 * @author goode 3 * @date 2017-09-30 14:10:00 4 */ 5 public class DoThings extends Thread { 6 7 private CyclicBarrier barrier; 8 9 private Integer time; 10 11 private Vector<String> vector; 12 13 public DoThings(Vector<String> vector, CyclicBarrier barrier, int time) { 14 this.barrier = barrier; 15 this.time = time; 16 this.vector = vector; 17 } 18 19 @Override 20 public void run() { 21 System.out.println("====子线程" + Thread.currentThread().getName() + "开始执行..."); 22 try { 23 Thread.sleep(time);//程序处理其他业务逻辑 24 vector.add(Thread.currentThread().getName()); 25 barrier.await(); 26 } catch (BrokenBarrierException e) { 27 e.printStackTrace(); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 System.out.println("====子线程" + Thread.currentThread().getName() + "执行完成。"); 32 } 33 34 }
1 public static void main(String[] args) { 2 System.out.println("====主线程" + Thread.currentThread().getName() + "开始...."); 3 ExecutorService es = Executors.newFixedThreadPool(5); 4 final Vector<String> vector = new Vector<>(); 5 CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() { 6 @Override 7 public void run() { 8 System.out.println("====所有子线程执行完毕,获取处理后的数据长度=" + vector.size()); 9 } 10 }); 11 for (int i = 1; i <= 5; i++) { 12 es.execute(new DoThings(vector, barrier, i*1000)); 13 } 14 es.shutdown(); 15 }
程序处理结果:
====主线程main开始.... ====子线程pool-1-thread-1开始执行... ====子线程pool-1-thread-5开始执行... ====子线程pool-1-thread-3开始执行... ====子线程pool-1-thread-4开始执行... ====子线程pool-1-thread-2开始执行... ====所有子线程执行完毕,获取处理后的数据长度=5 ====子线程pool-1-thread-5执行完成。 ====子线程pool-1-thread-1执行完成。 ====子线程pool-1-thread-2执行完成。 ====子线程pool-1-thread-3执行完成。 ====子线程pool-1-thread-4执行完成。
四、结果分析
主要对比CountDownLatch和CyclicBarrier:
1、使用场景
从它们的的await()方法的位置即可看出,
CountDownLatch是主线程wait所有子线程工作结束之后执行主线程。
CyclicBarrier中是所有子线程之间相互wait,直到所有子线程就绪之后,如果有BarrierAction,则接着执行BarrierAction,最后再一起执行await()之后的程序。
2、释放子线程资源时间点
CountDownLatch中的每个子线程执行完之后,总线程统计计数减一,直到计数为0时释放所有等待的线程,不可重用。
CyclicBarrier中的每个子线程执行完之后,总线程统计计数加一,直到计数达到指定值时释放所有等待的线程。计数达到指定值时,计数置为0重新开始,以达到 CyclicBarrier重用的功能。
3、异常处理
CountDownLatch中某个子线程中遇到异常,不影响其他子线程运行。
CyclicBarrier中某个子线程中遇到异常,其他子线程也将抛出异常。
4、建议:
如果业务需求非常严谨,不允许任何一个子线程出行异常,则应该使用CyclicBarrier较为妥当,而如果CyclicBarrier的BarrierAction不依赖于其他子线程运行的结果,
则任何一个线程被释放的之后,即可直接运行这个Action(可用barrier.await()==2判断)。
实际使用过程中,可根据业务需求,适当调整countDown()、await()方法所在程序中的位置,可达到事半功倍的效果。