Callable 接口

Callable 接口 是 java.util.concurrent.下的一个泛型接口 , 只有一个call () 方法 , 它是有返回值的 ,  我们可以获取多线程执行的结果 , 使用 Callable接口 和 FutureTask 的组合 , 可以实现利用 FutureTask 来跟踪异步计算的结果 

 

获取多线程的方式


1. 继承 Thread 类

2. 实现 Runnable 接口

3. 使用 Callable 接口   ( FutureTask + Callable )

 

Runnable 接口 和 Callable 接口的区别 ?

1. Runnable 接口 没有返回值 , Callable 接口有返回值

2. Runnable 接口 有抛异常 , Callable 接口没有抛异常

3. Runnable 接口 的方法是 run ( ) , Callable 接口的 方法是 call ( )

特别重要的一个区别 :  Callable 实现了 使用 FutureTask 异步计算的操作 , 就是 可以检测方法是否完成 , 以及计算的结果值 , 可以根据 Future 对象 了解线程在执行任务中的情况

 

Callable 接口直接替换 Runnable 是否可行 ?

不可行 ! ! !

因为 : thread 类的构造方法中根本就没有 Callable 这个类型的参数 ,  这时我们无法直接使用 Callable 来实现多线程的

 

这时我们需要找一个媒介, 一个中间人 : 利用了 java 多态 ,一个类可以实现多个接口 !!

这个中间人就叫 : FutureTask

 

如何使用 callable接口 实现多线程 操作 资源类 的流程以及思想

1. 我们无法直接传入一个 callable 的接口类型 , 这时需要找到一个中间人 , 将他们联系起来

2. 我们知道 Thread 类中可以传入一个 Runnable 接口 , 所以根据这个就可以传入 Runnable的实现子类或者是Runnable 的一些子接口 , 可以发现它有一个叫做 RunnableFuture 子接口,这个子接口还有一个实现子类 叫做 FutureTask (中间人)

3. 这个 FutureTask类 , 他有一个构造方法 , FutureTask (Callable<V> callable) , 这时我们可以发现 , 它能够传入一个 Callable 接口类型的

4. 这时就连接上了 , 我在 Thread 中传入一个 futureTask (相当于 Runnable 的孙子)

5. Thread 就间接性的 达成了通过使用 Callable 接口 + FutureTask 来实现多线程操作资源类

根据以上代码,可以看到两个类如果想要产生关联 , 可以使用这样的思想 :

1. 继承或者实现了某些类或者某些接口 ,

2. 可以在一个类中引入/声明另外一个类的实例

3. 构造注入 , 一个类的构造器中 , 可以传入另一个类的实例

public class CASTest666 {
    private int ticket = 50;
    private int i = 1;
    
    public synchronized void sale() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "再卖第" + "\t" + i++ + "张票还有" + --ticket + "\t" + "张票");
        }
    }
}

class TestMythread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CASTest666 casTest666 = new CASTest666();
        FutureTask futureTask = new FutureTask<>(new Callable<Object>() {
            @Override
            public String call() throws Exception {
                for (int i = 0; i < 50; i++) {
                    casTest666.sale();
                }
                return "我是callable实现的多线程的操作";
            }
        });
        Thread thread = new Thread(futureTask, "A");
        thread.start();
        // 运行成功后获得返回值:调用get方法
        System.out.println(futureTask.get());
    }
}

运行成功后还可以获取返回值 , 调用 get 方法

 

那么 , FutureTask 是什么 ?


FutureTask 见名知意 : " 未来的任务 " , 就是使用它就处理一件比较复杂的事情 , 异步调用 , 在不影响主线程的正常执行的前提下 , 另启一条线程去做某些复杂耗时的事情 , 最后处理完成后在和主线程汇总

打个比方 :

就像老师上着课,口渴了, 想喝水,老师如果去买水, 没有老师了, 同学就要等待老师回来 , 此时无法上课 , 出现了阻塞 , 不太合适,那么怎么办 ? 我可以单起一个线程 , 例如 : 找班长 (FutureTask) 帮忙买水 ,老师 (main) 还能继续讲课 , 就不会发生阻塞的问题 , 水买回来了放桌子上,当老师需要的时候再去拿就可以了(get方法)

FutureTask 的原理


  1. 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给   FutureTask 对象 在后台完成
  2. 当主线程将来需要时,就可以通过 FutureTask 对象 获得后台作业的计算结果或者执行状态。
  3. 一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
  4. 仅在计算完成时才能检索结果 , 如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。get 方法获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。只计算一次 , 因此需要将 get 方法放到最后 , 否则会出现阻塞

注意 : 如果是两个线程 操作同一个资源类时 , FutureTask 的特点就是在高并发环境下确保任务只执行一次 , 我们仅仅只是可以多次的 get 取值

class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
       
         FutureTask futureTask = new FutureTask(() -> {
            System.out.println(Thread.currentThread().getName() + "Callable1 我来咯");
            TimeUnit.SECONDS.sleep(4);
            return 1024;
        });
        
        FutureTask<Integer> futureTask2 = new FutureTask(() -> {
            System.out.println(Thread.currentThread().getName() + "  Callable2 我来咯");
            TimeUnit.SECONDS.sleep(4);
            return 2048;
        });


//        两个线程通过实现Callable接口的方式来操作不同的资源类
        new Thread(futureTask, "线程1").start();   // 线程1 Callable1 我来咯
        new Thread(futureTask2, "线程2").start();  // 线程2 Callable2 我来咯

//       注意 get 方法写在了 main 线程的前面, 在计算的时候会发生阻塞 ,知道计算完成 get调用完毕 才会执行main线程
//       一般将 get 方法放到最后,否则会出现阻塞,导致主线程等待计算的过程
        System.out.println(futureTask.get());   // 1024
        System.out.println(futureTask2.get());  // 2048
        System.out.println(Thread.currentThread().getName() + " come over");  // main come over
    }
}

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值