CountDownLatch和Future为什么没有获取到期望的结果呢?

最近在项目中使用CountDownLatch和Future开发,经过并发测试,发现有些数据结果并没有按照期望获取到,这是为什么呢?
jdk1.8.0_112
代码如下(涉及到项目信息,以下代码是按照项目中抽取):

package me.qianlv;

import java.util.List;
import java.util.concurrent.*;

/**
 * CountDownLatch 测试
 *
 * @author xiaoshu
 */
public class CountDownLatchTwo {
    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            testResult();
        }
    }

    public static void testResult() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        Future<String> one = executorService.submit(() -> {
            try {
                return "ONE";
            } finally {
                latch.countDown();
            }
        });
        Future<String> two = executorService.submit(() -> {
            try {
                return "TWO";
            } finally {
                latch.countDown();
            }
        });
        Future<String> three = executorService.submit(() -> {
            try {
                return "Three";
            } finally {
                latch.countDown();
            }
        });

        latch.await(1000, TimeUnit.MILLISECONDS);

        //项目中,通过判断线程是否执行完成,获取线程结果
        if (!one.isDone()) {
            System.out.println(" 线程1没有运行完");
        }

        if (!two.isDone()) {
            System.out.println(" 线程2没有运行完");
        }

        if (!three.isDone()) {
            System.out.println(" 线程3没有运行完");
        }
    }
}

通过1000次循环请求模拟,输出结果如下:


image.png

预期的应该是,所有的线程都运行结果,不应该有任何输出,但是,从结果来看,确实有输出!!!

后来分析原因,突然想到了,finally其实是在return之前执行!!!也就是说,当CountDownLatch解锁的时候,return语句可能未被执行

使用总结:
使用CountDownLatch、CyclicBarrier这类控制并发等待的工具类时,不应该采用Future返回值的方式。

可采用线程外传对象,修改对象的方式。如:

package me.qianlv;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * CountDownLatch 测试
 *
 * @author xiaoshu
 */
public class CountDownLatchTwo {
    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            testResult();
        }
    }

    public static void testResult() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        Result oneResult = new Result();
        executorService.execute(() -> {
            try {
                oneResult.setMessage("ONE");
            } finally {
                latch.countDown();
            }
        });
        Result twoResult = new Result();
        executorService.execute(() -> {
            try {
                twoResult.setMessage("TWO");
            } finally {
                latch.countDown();
            }
        });
        Result threeResult = new Result();
        executorService.execute(() -> {
            try {
                threeResult.setMessage("Three");
            } finally {
                latch.countDown();
            }
        });

        latch.await(1000, TimeUnit.MILLISECONDS);

        //项目中,通过判断线程是否执行完成,获取线程结果
        if (null == oneResult.getMessage()) {
            System.out.println("线程1没有设置值");
        }

        if (null == twoResult.getMessage()) {
            System.out.println("线程2没有设置值");
        }

        if (null == threeResult.getMessage()) {
            System.out.println("线程3没有设置值");
        }
    }
}

class Result {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

后者使用ExecutorService.submit(Runnable task, T result)方法,该方法比较简单,不再演示了。

推荐阅读更多精彩内容