多线程九 线程池之Future接口

线程池之Future接口

1. Future

Future接口中的get()会阻塞当前线程直到取得Callable的返回值

API文档

FutureTask 类是实现 Future ,实现 Runnable ,所以可以由 Executor 执行

FutureTask 可用于包装 Callable 或 Runnable 对象. 因为 FutureTask 实现 Runnable ,一个FutureTask可以提交到一个Executor执行

在这里插入图片描述
要想讲的清楚多线程,怎么能少得了我们的12306呢?今天,又是卖火车票的一天…
在这里插入图片描述
在这里插入图片描述

package com.ThreadPool;

import java.util.concurrent.*;

/**
 * @Author: Mr.Q
 * @Date: 2019-08-05 09:02
 * @Description:
 */
class CallableThread implements Callable<String> {
    private Integer tickets = 30;
    @Override
    public String call() throws Exception {
        for (int i = 0; i < tickets; i++) {
            if(tickets > 0) {
                System.out.println(Thread.currentThread().getName() +"还剩 "+tickets--+" 票...");
            }
        }
        return Thread.currentThread().getName() + "票卖完了!";
    }
}

public class FutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 线程池创建线程
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(2,5,
                        60,TimeUnit.SECONDS,
                        new LinkedBlockingDeque<Runnable>());

        CallableThread callableThread = new CallableThread();
        // Future接口接收 Callable的返回值
        Future<String> future = null;
        for (int i = 0; i < 5; i++) {
        // submit()提交需要返回值的任务
             future = threadPoolExecutor.submit(callableThread);
        }
        //----------------- main - ---------------
        System.out.println(future.get());
        threadPoolExecutor.shutdown();
    }
}

get()会阻塞当前线程直到取得Callable的返回值

此时,在main()中调用get(),导致当前线程(主线程)阻塞,直到线程池中的子线程执行完毕后,在执行!

若此时我们把 get() 放在提交线程任务时调用

 Future<String> future = null;
        // 单线程模式 get()会阻塞当前线程,直到有返回值为止
       for (int i = 0; i < 5; i++) {
           Future<String> future0 = threadPoolExecutor.submit(callableThread);
           System.out.println(future0.get());
       }

那么此时阻塞当前提交任务的线程,也就是说,它没有提交完任务,其他的线程都不能执行,都得等着. 表面上看我们在最大池中创建了5个线程,可实际上执行任务的只有一个,其他的都在阻塞. 此时为 单线程模式

2. FutureTask

FutureTask : 可以保证在多线程场景下,任务只会被一个线程执行,其他线程不再执行此任务;
在多线程并发下可以保证任务(传入的CallableRunnable)只执行一次

什么意思呢? 就是我现在要给在火车站柜台买票的每一个人,买一张票,我就请他去候车室喝咖啡(虽然这是不可能的)

如果,我让一个乘务员来做这件事(把乘务员抽象的看作是一个线程),他先得乘客卖票,然后再给乘客到候车室冲咖啡递上去,是不是很麻烦,效率很低?这要是到春运了,估计回家了,也就开学了…

那我再雇一个乘务员,一个负责卖票,另一个负责在候车室服务乘客,效率高,分工明确.
在这里插入图片描述

那么此时再看我们FutureTask的作用

可以保证在多线程场景下,任务只会被一个线程执行,其他线程不再执行此任务

相当于负责售票的乘务员A不管服务乘客,负责服务乘客的乘务员B不管卖票;

而开始的时候,这两件事情都由乘务员C来负责,两个任务都得执行.

这就是FutureTask的应用场景

在这里插入图片描述
最近也没怎么敲代码,但是头发却是哗哗的往下掉呀. 养生与学习并重,少熬夜,多运动. 然后,没事的时候,少喝点肥仔快乐水,多喝茶养生.

那我们就用FutureTask来实现一下泡茶的场景吧
在这里插入图片描述现在,两个线程负责不同的任务,并且有一个线程在执行任务后,其他线程就不再执行任务

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Mr.Q
 * @Date: 2019-08-07 09:17
 * @Description:
 */
// 线程1任务
class StepOne implements Callable<String> {
    private FutureTask<String> futureTask;

    public StepOne(FutureTask<String> futureTask) {
        this.futureTask = futureTask;
    }

    @Override
    public String call() throws Exception {
        System.out.println("T1:洗水壶.");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("T1:烧开水..");
        TimeUnit.SECONDS.sleep(10);
        System.out.println("T1:等待茶叶...");
        // 线程1阻塞到此处一直等到线程2返回为止
        String str = futureTask.get();
        System.out.println("T1:拿到茶叶....");
        System.out.println("泡茶");
        return "上茶~~~";
    }
}

// 线程2任务
class StepTwo implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("T2:洗茶壶*");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("T2:洗茶杯**");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("T2:拿茶叶***");
        TimeUnit.SECONDS.sleep(3);
        return "龙井";
    }
}

public class FutureThreadPoolDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask step2 = new FutureTask(new StepTwo());
        //将线程2的龙井茶传给线程1
        FutureTask step1 = new FutureTask(new StepOne(step2));
        new Thread(step1).start();
        new Thread(step2).start();
        // 主线程在step1,step2执行完之后再执行
        System.out.println(step1.get());
    }
}

执行过程:
在这里插入图片描述
好了,今天的养生课到这里就结束了,下课吧同学们…

发布了77 篇原创文章 · 获赞 118 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43232955/article/details/99226671
今日推荐