FutureTask详解及典型用例----高并发实现烧水泡茶

FutureTask详解及典型用例----高并发实现烧水泡茶

Future接口:做用为获取Callable接口的返回值java

FutureTask类为Future接口子类,该类独有的特色为在高并发状况下不论有多少个线程,均只执行一次任务。web

get()方法的做用为阻塞当前线程直到有返回值为止。安全

使用Future接口中get()方法的两种状况:并发

(1)提交线程的同时调用get()方法ide

class CallableTest implements Callable<String>{
    private static Integer ticket=10;
    @Override
    public String call() throws Exception {
        while (ticket>0){
            System.out.println(Thread.currentThread().getName()+"还剩"+ticket--+"张票");
        }
        return "票已经卖完啦";
    }
}
public class ThreadTest{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service=Executors.newCachedThreadPool();
        Callable<String> callable=new CallableTest();
        for(int i=0;i<5;i++){
           String str=service.submit(callable).get();
           System.out.println(str);
        }
        service.shutdown();
    }
}
/** pool-1-thread-1还剩10张票 pool-1-thread-1还剩9张票 pool-1-thread-1还剩8张票 pool-1-thread-1还剩7张票 pool-1-thread-1还剩6张票 pool-1-thread-1还剩5张票 pool-1-thread-1还剩4张票 pool-1-thread-1还剩3张票 pool-1-thread-1还剩2张票 pool-1-thread-1还剩1张票 票已经卖完啦 票已经卖完啦 票已经卖完啦 票已经卖完啦 票已经卖完啦 **/

//使用FutureTask类实现
class CallableTest implements Callable<String>{
    private static Integer ticket=10;
    @Override
    public String call() throws Exception {
        while (ticket>0){
            System.out.println(Thread.currentThread().getName()+"还剩"+ticket--+"张票");
        }
        return "票已经卖完啦";
    }
}
public class ThreadTest{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask=new FutureTask<>(new CallableTest());
        for(int i=0;i<5;i++){
           new Thread(futureTask).start();
        }
    }
}
/** Thread-1还剩10张票 Thread-1还剩9张票 Thread-1还剩8张票 Thread-1还剩7张票 Thread-1还剩6张票 Thread-1还剩5张票 Thread-1还剩4张票 Thread-1还剩3张票 Thread-1还剩2张票 Thread-1还剩1张票 */

由以上代码咱们能够看到,get()方法阻塞了主线程,线程1得到锁后执行完任务并返回返回值,其余线程不执行任务只返回返回值。svg

(2)提交线程以后调用get()方法高并发

class CallableTest implements Callable<String>{
    private static Integer ticket=10;
    @Override
    public String call() throws Exception {
        while (ticket>0){
            System.out.println(Thread.currentThread().getName()+"还剩"+ticket--+"张票");
        }
        return "票已经卖完啦";
    }
}
public class ThreadTest{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service=Executors.newCachedThreadPool();
        Callable<String> callable=new CallableTest();
        Future<String> future=null;
        for(int i=0;i<5;i++){
           future=service.submit(callable);
        }
        String str=future.get();//阻塞主线程,高并发执行任务,不安全
        System.out.println(str);
        service.shutdown();
    }
}
/** pool-1-thread-1还剩10张票 pool-1-thread-2还剩9张票 pool-1-thread-1还剩8张票 pool-1-thread-2还剩7张票 pool-1-thread-3还剩5张票 pool-1-thread-1还剩6张票 pool-1-thread-3还剩3张票 pool-1-thread-2还剩4张票 pool-1-thread-3还剩1张票 pool-1-thread-1还剩2张票 票已经卖完啦 **/

注意:支持处理Callable对象的两种方法
(1)FutureTask:高并发状况下线程任务只执行一次
(2)线程池的submit()方法可向线程池中提交Callable对象this

FutureTask典型用例----高并发实现烧水泡茶实现流程图:

在这里插入图片描述
解题思路:spa

经过流程图的分析咱们不难看出,烧水与洗茶壶、洗茶杯、拿茶叶是能够并行的,两线程在泡茶处有交集。咱们能够将横行看做线程1,竖行看做线程2,所以线程1必须等待线程2执行完毕后才可执行泡茶操做(泡茶必须在拿到茶叶以后),故需在线程1中调用线程2的get方法对线程1进行阻塞,直到线程2完成任务后方可泡茶。线程

实现源代码:

//执行线程1任务
class Task1 implements Callable<String>{
    private FutureTask<String> futureTask;
    public Task1(FutureTask<String> futureTask) {//经过构造方法将线程2传入
        this.futureTask = futureTask;
    }
    @Override
    public String call() throws Exception {
        System.out.println("T1:洗水壶...");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("T1:烧开水...");
        TimeUnit.SECONDS.sleep(15);
        String str=futureTask.get();//调用线程2的get()方法用于阻塞当前线程直到线程2任务结束
        System.out.println("T1:泡茶");
        return "上茶";
    }
}
//执行线程2任务
class Task2 implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("T2:洗茶壶");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("T2:洗茶杯");
        TimeUnit.SECONDS.sleep(2);
        System.out.println("T2:拿茶叶");
        TimeUnit.SECONDS.sleep(1);
        return "上好龙井";
    }
}
public class ThreadTest{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
       FutureTask<String> ft2=new FutureTask<>(new Task2());
       FutureTask<String> ft1=new FutureTask<>(new Task1(ft2));
       new Thread(ft1).start();
       new Thread(ft2).start();
       System.out.println(ft1.get());//用于阻塞主线程
    }
}

/** T1:洗水壶... T2:洗茶壶 T1:烧开水... T2:洗茶杯 T2:拿茶叶 T1:泡茶 上茶 **/