zoukankan      html  css  js  c++  java
  • Android(java)学习笔记6:实现Runnable接口创建线程 和 使用Callable和Future创建线程

    1. 前面说的线程的实现是新写一个子类继承Thread:

    将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例

    2. 这里说的方案2是指实现一个接口:

    声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。

    这里我们主要说明2的实现方式…………

     1 package cn.itcast_05;
     2 
     3 public class MyRunnable implements Runnable {
     4 
     5     @Override
     6     public void run() {
     7         for (int x = 0; x < 100; x++) {
     8             // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
     9             System.out.println(Thread.currentThread().getName() + ":" + x);
    10         }
    11     }
    12 
    13 }
     1 package cn.itcast_05;
     2 
     3 /*
     4  * 方式2:实现Runnable接口
     5  * 步骤:
     6  *         A:自定义类MyRunnable实现Runnable接口
     7  *         B:重写run()方法
     8  *         C:创建MyRunnable类的对象
     9  *         D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
    10  */
    11 public class MyRunnableDemo {
    12     public static void main(String[] args) {
    13         // 创建MyRunnable类的对象
    14         MyRunnable my = new MyRunnable();
    15 
    16         // 创建Thread类的对象,并把C步骤的对象作为构造参数传递
    17         // Thread(Runnable target)
    18         // Thread t1 = new Thread(my);
    19         // Thread t2 = new Thread(my);
    20         // t1.setName("林青霞");
    21         // t2.setName("刘意");
    22 
    23         // Thread(Runnable target, String name)
    24         Thread t1 = new Thread(my, "林青霞");
    25         Thread t2 = new Thread(my, "刘意");
    26 
    27         t1.start();
    28         t2.start();
    29     }
    30 }

    3. 方案3:使用Callable和Future创建线程

      前面实现Runnable接口创建多线程时候,Thread类的作用是把run()方法包装成线程执行体。那么是否可以直接把任意方法都包装成线程执行体呢?

    Java目前不行的,但是Java的模仿者C#可以(C#可以将任意方法包装成线程执行体,包括有返回值的方法)

      也许受此启发,从Java5开始,Java提供了Callable接口,该接口可以看成是Runnable接口的增强版,Callable接口提供了一个call()方法可以作为线程执行体,但是call()方法比run()方法功能更加强大。

    • call()方法可以有返回值
    • call()方法可以声明抛出异常

    因此完全可以提供一个Callable对象作为Thread的target,而该线程的线程执行体就是该Callable对象的call()方法。

      问题是:Callable接口是Java 5新增的接口,而且它不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target。而且call()方法还有一个返回值---call()方法并不是直接调用的,它是作为线程执行体被调用的,那么如何获取call()方法的返回值呢?

    Java 5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供一个FutureTask实现类,该实现类实现了Future接口,并实现了Runnable接口---可以作为Thread类的target。

      在Future接口里定义了如下几个公共方法来控制它关联的Callable任务:

    • boolean cancel(boolean mayInterruptIfRunning):试图取消该Future里关联的Callable任务。
    • V get():返回Callable任务里面call()方法的返回值。调用该方法将导致程序阻塞,必须等到子线程结束之后才会得到返回值
    • V get(long timeout, TimeUnit unit):返回Callable任务里call()方法的返回值。该方法让程序最长阻塞时间由timeout 和 unit指定的时间,如果经过指定时间后Callable任务依然没有返回值,将会抛出TimeoutException异常
    • boolean isCancelled():如果在Callable任务正常完成前取消,则返回true。
    • boolean isDone():如果Callable任务已完成,则返回true。

    使用Callable和Future创建线程的步骤

    (1)创建Callable接口实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,在创建Callable实现类的实例。

    (2)使用FutureTask类包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值

    (3)使用FutureTask对象作为Thread对象target创建并启动新线程。

    (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

    示例1:

     1 package com.himi.threadcallable;
     2 
     3 import java.util.Random;
     4 import java.util.concurrent.Callable;
     5 import java.util.concurrent.ExecutionException;
     6 import java.util.concurrent.FutureTask;
     7 
     8 public class CallableAndFutureDemo1 {
     9     public static void main(String[] args) {
    10         
    11         Callable<Integer> callable = new Callable<Integer>() {
    12             public Integer call() throws Exception {
    13                 return new Random().nextInt(100);
    14             }
    15         };
    16         
    17         FutureTask<Integer> future = new FutureTask<Integer>(callable);
    18         
    19         new Thread(future).start();
    20         
    21         try {
    22             Thread.sleep(5000);// 可能做一些事情
    23             System.out.println(future.get());//子线程执行结束,获取返回值
    24         } catch (InterruptedException e) {
    25             e.printStackTrace();
    26         } catch (ExecutionException e) {
    27             e.printStackTrace();
    28         }
    29     }
    30 }

    执行结果,如下:

    FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,岂不美哉!

    示例2:

     1 package com.himi.threadcallable;
     2 
     3 import java.util.Random;
     4 import java.util.concurrent.Callable;
     5 import java.util.concurrent.ExecutionException;
     6 import java.util.concurrent.ExecutorService;
     7 import java.util.concurrent.Executors;
     8 import java.util.concurrent.Future;
     9 
    10 public class CallableAndFutureDemo2 {
    11     public static void main(String[] args) {
    12         ExecutorService threadPool = Executors.newSingleThreadExecutor();
    13         Future<Integer> future = threadPool.submit(new Callable<Integer>() {
    14             public Integer call() throws Exception {
    15                 return new Random().nextInt(100);
    16             }
    17         });
    18         try {
    19             Thread.sleep(5000);// 可能做一些事情
    20             System.out.println(future.get());
    21         } catch (InterruptedException e) {
    22             e.printStackTrace();
    23         } catch (ExecutionException e) {
    24             e.printStackTrace();
    25         }
    26     }
    27 }

    运行效果,如下:

    代码是不是简化了很多,ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发编程,Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。 

    4. 总结:

    实现多线程的方式:3种

    • 方式1:继承Thread类
            A:自定义类MyThread继承Thread类
            B:在MyThread类中重写run().
            C创建MyThread类的对象.
            D 启动线程对象

        问题
               a为什么要重写run()方法?
                  run()里面封装的是被线程执行的代码
               b启动线程对象要哪个方法?
              start()
               crun()和start()方法的区别?
                    run()直接调用的仅仅是普通方法
                      start()先启动线程,再由JVM调用run()方法

    • 方式2:实现Runnable接口
        A自定义类MyRunnable实现Runnable接口.
                B在MyRunnable里面重写run()
                C:创建MyRunnable类的对象
                D创建Thread类的对象,并把C步骤的对象作为构造参数传递

    • 方式3:使用Callable 和 Future创建线程

        步骤在上面。


        问题来了1:有了方式1,为什么还来方式2呢 ?
        a可以避免由于Java单继承带来的局限性.
             b适合多个相同程序代码去处理同一个资源的情况,把线程同程序的代码、数据有效分离较好的体现了面向对象的设计思想。

       

  • 相关阅读:
    Abaqus刚体建模方法
    Abaqus 幅值曲线—与时间相关的函数定义
    Abaqus/CAE 热-力耦合分析
    数据库设计要遵循的点
    c# 面向对象的编程思想
    领导要求你去做不属于你工作范围内的工作,你是答应还是委婉拒绝?
    工作总结&成长感悟
    jquery中的ajax方法参数
    swal() 弹出删除确认框
    serializearray()的具体使用
  • 原文地址:https://www.cnblogs.com/hebao0514/p/4508516.html
Copyright © 2011-2022 走看看