代码前提:
BillElectronicItemAttachment只是一个普通的实体类
CountDownLatch+ThreadPool代码
线程代码:
package com.tuniu.app.thread; import com.tuniu.BillElectronicItemAttachment; import java.util.List; import java.util.concurrent.CountDownLatch; public class UploadImageThread implements Runnable{ public static final String IMAGE_START = "image/"; private List<BillElectronicItemAttachment> resultList; private CountDownLatch countDownLatch; private String paramStr; public UploadImageThread(List<BillElectronicItemAttachment> resultList, CountDownLatch countDownLatch,String paramStr) { this.resultList = resultList; this.countDownLatch = countDownLatch; this.paramStr = paramStr; } @Override public void run() { try { BillElectronicItemAttachment billElectronicItemAttachment = new BillElectronicItemAttachment(); billElectronicItemAttachment.setFileName(paramStr); resultList.add(billElectronicItemAttachment); Thread.sleep(2); }catch (Exception e) { e.printStackTrace(); }finally { countDownLatch.countDown(); } } }
测试方法:
private static void countThreadTest(ThreadPoolExecutor threadPoolExecutor, CountDownLatch countDownLatch, List<BillElectronicItemAttachment> list) throws InterruptedException { for (int i = 0; i < 2000; i++) { Integer v = (int) (Math.random() * 100000); threadPoolExecutor.execute(new UploadImageThread(list, countDownLatch, v.toString())); } countDownLatch.await(); }
FutureTask+ThreadPool 代码:
线程代码:
package com.tuniu.app.thread; import java.util.concurrent.Callable; public class UploadImageThreadSubmit implements Callable<String> { private String paramStr; public UploadImageThreadSubmit(String paramStr) { this.paramStr = paramStr; } @Override public String call() throws InterruptedException { Thread.sleep(2); return paramStr; } }
测试方法:
private static void callableTest(ThreadPoolExecutor threadPoolExecutor, List<BillElectronicItemAttachment> list) throws ExecutionException, InterruptedException { List<FutureTask> futureTasks = new ArrayList<FutureTask>(); for (int i = 0; i < 2000; i++) { Integer v = (int) (Math.random() * 100000); FutureTask<String> stringFutureTask = new FutureTask<String>(new UploadImageThreadSubmit(v.toString())); threadPoolExecutor.execute(stringFutureTask); futureTasks.add(stringFutureTask); } for (FutureTask futureTask : futureTasks) { BillElectronicItemAttachment billElectronicItemAttachment = new BillElectronicItemAttachment(); billElectronicItemAttachment.setFileName((String) futureTask.get()); list.add(billElectronicItemAttachment); } }
测试代码:
public static void main(String[] args) throws InterruptedException, ExecutionException { for (int i = 0; i < 20; i++) { long start = System.currentTimeMillis(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 8, 10000L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100), new ThreadPoolExecutor.CallerRunsPolicy()); CountDownLatch countDownLatch = new CountDownLatch(20); List<BillElectronicItemAttachment> list = Collections.synchronizedList(new ArrayList<BillElectronicItemAttachment>()); countThreadTest(threadPoolExecutor, countDownLatch, list); // callableTest(threadPoolExecutor, list); System.out.println("我结束了:" + (System.currentTimeMillis() - start)); threadPoolExecutor.shutdown(); } }
结果对比:
结论:
CountDownLatch+ThreadPool稍快,无论是第一次执行,还是后续的执行
原因:
FutureTask+ThreadPool 最后有一步循环获取的动作 billElectronicItemAttachment.setFileName((String) futureTask.get()); ,很费时间
使用CountDownLatch+ThreadPool必读:
正常情况下,在 Java 中入参是不建议用做返回值的。除了造成代码不易理解、语义不清等问题外,可能还埋下了陷阱等你入坑。
先来看下编程语言中关于参数传递的类型:
-
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
-
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
一旦传入的对象被重新赋值,那就无法拿到结果;
例如:
@Override public void run() { try { resultList = new ArrayList<>(); Thread.sleep(2); }catch (Exception e) { e.printStackTrace(); }finally { countDownLatch.countDown(); } }
类似的处理还有 String a = "ww";
举一个jdk8易出错的场景:
resultList = resultList.stream().collect(Collectors.toList());
所以使用 java8 的时候一定要谨慎