Java并发编程——线程池的使用(七)线程池的可执行任务:Future和Callable

一、回顾线程池的创建

首先我们先来回顾下创建线程池:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());

线程池的submit可以提交一个Callable任务,Future的get方法可以获取到Callable的call()方法的返回值,看下一小节:

二、线程池提交Callable任务

Callable接收一个泛型,我们可以根据项目需要自由传入泛型,这里我们以最简单的String为例。
首先我们创建一个类:TestCallable

public class TestCallable implements Callable<String> {

    private String name;

    public TestCallable(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        return getName();
    }

    private String getName() {
        try {
            //模拟耗时操作
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return name + "123";
    }
}

注解里面写的很详细,这里注意下模拟的耗时操作,睡眠了1秒,目的是稍后我们在执行线程池任务的时候,为了验证get()方法有阻塞作用。

 public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
            TestCallable testCallable = new TestCallable("张三");
        Future<String> future = executor.submit(testCallable);
        try {
            //返回call()方法的返回值,有阻塞作用
            String name = future.get();
            System.out.println("name = " + name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //为了证明get()方法有阻塞作用,在这里输出一条语句
        System.out.println("执行了,看看我在get()方法前面还是后面执行~~~");
    }

接下来看下log:

12-28 10:00:34.410 11613-11613/lbx.myapplication I/System.out: name = 张三123
12-28 10:00:34.410 11613-11613/lbx.myapplication I/System.out: 执行了,看看我在get()方法前面还是后面执行~~~

通过log我们可以发现,get()方法确实用阻塞效果,当任务执行完后,释放阻塞,然后返回任务的处理结果。

三、线程池提交Runnable任务并获取Future的返回结果

线程池中有一个方法:

Future<Result> future = executor.submit(Runnable, result);

第二个参数是执行结果的返回值。这么说可能有些抽象,我们来举个小栗子:
创建一个Bean类,作为Result的基类:

public class TestBean {
    public String name;
}

接下来创建一个Runnable任务:

public class TestRunnable implements Runnable {
    private TestBean testBean;

    public TestRunnable(TestBean testBean) {
        this.testBean = testBean;
    }

    @Override
    public void run() {
        testBean.name = "张三";
        System.out.println("TestRunnable");
    }
}

我们把TestBean 传进来,这个Runnable的任务就是,把TestBean 的name属性赋值。
接下来:

    public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        TestBean testBean = new TestBean();
        Future<TestBean> future = executor.submit(new TestRunnable(testBean), testBean);
        try {
            //注意有阻塞效果
            TestBean bean = future.get();
            System.out.println("bean = " + bean.name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

log:

12-28 13:12:54.919 4321-4466/lbx.myapplication I/System.out: TestRunnable
12-28 13:12:54.919 4321-4321/lbx.myapplication I/System.out: bean = 张三

我们发现 通过get()方法或得到的返回结果,是赋值以后的TestBean 。

四、常用方法总结

//没有阻塞作用,future任务是否完成
future.isDone();
//取消正在进行的任务,可以配合Thread.currentThread().isInterrupted()和InterruptedException在Callable的call方法里使用
future.cancel(true);
//是否取消成功
future.isCancelled();
//在n秒内获取,超过时长阻塞效果消失并抛出异常
future.get(n, TimeUnit.SECONDS);

没有更多推荐了,返回首页