Juc01_多线程概述、四种实现方式、常用方法API、生命周期、买票案例、synchronized锁(二)

简介: ③. Callable接口(创建线程)

③. Callable接口(创建线程)


  • ①. Callable接口中的call方法和Runnable接口中的run方法的区别


  1. 是否有返回值(Runnable接口没有返回值 Callable接口有返回值)


  1. 是否抛异常(Runnable接口不会抛出异常 Callable接口会抛出异常)


  1. 落地方法不一样,一个是call() ,一个是run()


微信图片_20220106173548.png


  • ②. Future接口概述


  1. FutureTask是Future接口的唯一的实现类


  1. FutureTask同时实现了Runnable、Future接口。它既可以作为Runnable被线程执行,又可以作为Futrue得到Callable的返回值


微信图片_20220106173612.png


  /*
  创建线程的方式三: 实现callable接口 ---JDK 5.0 新增
  1.创建一个实现Callable接口的实现类
  2.实现call方法,将此线程需要执行的操作声明在call()中
  3.创建callable接口实现类的对象
  4.将此callable的对象作为参数传入到FutureTask构造器中,创建FutureTask的对象
  5.将FutureTask对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用star
  6.获取callable接口中call方法的返回值
  * */
  public class ThreadNew {
      public static void main(String[] args) {
          //3.创建callable接口实现类的对象
          NumThead m=new NumThead();
          //4.将此callable的对象作为参数传入到FutureTask构造器中,创建FutureTask的对象
          FutureTask futureTask = new FutureTask(m);
          //5.将FutureTask对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
          //FutureTask类继承了Runnable接口
          //new Runnable = futrueTask;
          new Thread(futureTask).start();
          //6.获取callable接口中call方法的返回值
          try {
              //get()方法返回值即为FutureTask构造器参数callable实现类重写的call方法的返回值
              Object sum = futureTask.get();
              System.out.println("总和是:"+sum);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
  }
  //1.创建一个实现Callable接口的实现类
  class  NumThead implements Callable{
     // class  NumThead implements Callable<Integer>{
      //2.实现call方法,将此线程需要执行的操作声明在call()中
      @Override
      public Object call() throws Exception {
      //public Integer call() throws Exception {
          int sum=0;
          for(int i=1;i<=100;i++){
              System.out.println(i);
              sum+=i;
          }
          return sum;
      }
  }


③.FutureTask原理解析


有了Runnable,为什么还要有Callable接口?我们假设一共有四个程序需要执行,第三个程序时间很长 | Runnable接口会按照顺序去执行,会依次从上到下去执行,会等第三个程序执行完毕,才去执行第四个 | Callable接口会把时间长的第三个程序单独开启一个线程去执行,第1、2、4 线程执行不受影响


比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务。子线程就去做其他的事情,过一会儿才去获取子任务的执行结果


微信图片_20220106173642.png


    例子:
  (1). 老师上着课,口渴了,去买水不合适,讲课线程继续,我可以单起个线程找班长帮忙
  买水,水买回来了放桌上,我需要的时候再去get。
  (2). 4个同学,A算1+20,B算21+30,C算31*到40,D算41+50,是不是C的计算量有点大啊,
  FutureTask单起个线程给C计算,我先汇总ABD,最后等C计算完了再汇总C,拿到最终结果
  (3). 高考:会做的先做,不会的放在后面做


  • ④. 注意事项


  1. get( )方法建议放在最后一行,防止线程阻塞(一旦调用了get( )方法,不管是否计算完成都会阻塞)


  1. 一个FutureTask,多个线程调用call( )方法只会调用一次


  1. 如果需要调用call方法多次,则需要多个FutureTask


public class CallableDemo  {
    public static void main(String[] args) throws Exception{
        CallAble c=new CallAble();
        FutureTask<Integer> futureTask=new FutureTask<>(c);
        new Thread(futureTask,"线程A").start();
        new Thread(futureTask,"线程B").start();
        Integer integer = futureTask.get();
        System.out.println("integer = " + integer);
    }
}
class CallAble implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("欢迎你调用call方法");
        return 6;
    }
}


微信图片_20220106173725.png


  • ⑤. isDone()轮询(后面我们会用CompletableFuture来解决get( )阻塞的问题)


  1. 轮询的方式会消耗无畏的CPU资源,而且也不见得能及时地得到计算的结果


  1. 如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要阻塞


public class FutureTaskTest {
    public static void main(String[] args) throws Exception{
        FutureTask futureTask = new FutureTask(()->{
            try { TimeUnit.SECONDS.sleep(3);  } catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"\t"+"coming......");
            return 1024;
        });
        new Thread(futureTask).start();
        //1.果futureTask.get()放到main线程前面,会导致main线程阻塞
        //Object o = futureTask.get();
        /*Object o = futureTask.get();//不见不散,只要出现了get()方法就会阻塞
        System.out.println("不见不散,只要出现了get()方法就会阻塞,获取到的值为:"+o);*/
        //2.过时不候
//        System.out.println(Thread.currentThread().getName()+"\t"+"线程来了.....");
//        Object o2 = futureTask.get(2L, TimeUnit.SECONDS);
        //3.使用轮询
        while(true){
            if(futureTask.isDone()){
                System.out.println("使用轮询来解决,值为:"+futureTask.get());
                break;
            }else{
                System.out.println("阻塞中**********");
            }
        }
    }
}
相关文章
|
25天前
|
Java API 调度
Java 线程的生命周期
在JDK 1.5之前,线程的生命周期包括五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。JDK 1.5及之后增加了三种阻塞状态,共六种状态:新建、可运行、终止、锁阻塞、计时等待和无限等待。这些状态描述了线程在操作系统和JVM中的不同阶段。
Java 线程的生命周期
|
29天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
38 1
[Java]线程生命周期与线程通信
|
24天前
|
供应链 数据挖掘 API
电商API接口介绍——sku接口概述
商品SKU(Stock Keeping Unit)接口是电商API接口中的一种,专门用于获取商品的SKU信息。SKU是库存量单位,用于区分同一商品的不同规格、颜色、尺寸等属性。通过商品SKU接口,开发者可以获取商品的SKU列表、SKU属性、库存数量等详细信息。
|
30天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
30天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
19 2
|
30天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
19 1
|
30天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
|
30天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
28 1
|
30天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
38 1
|
30天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
25 1