代码前提:

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 的时候一定要谨慎

 

posted on 2021-09-14 17:06  幂次方  阅读(664)  评论(0)    收藏  举报