上一篇文章有应用到demo代码演示应用继承Thread类的线程创立形式

接下来讲一讲实现Runnable接口的形式

一、实现Runnable接口的形式


咱们之前提到创立多线程的形式中,有实现Runnable接口的形式

class Demo2 implements  Runnable{    @Override    public void run() {        System.out.println("线程执行起来了.....");    }}

在上一篇文章中Demo1继承Thread时,间接创立即能够调用启动它

这时咱们的Demo2就是一个一般类实现了Runnable接口而已,那么如何来启动这个线程呢?

咱们上一篇文章中有提到Thread的实例化各种状况,其中就能承受Runnable接口的形式

public class Thread implements Runnable {        public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);}           Thread(Runnable target, AccessControlContext acc) {init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);}    public Thread(Runnable target, String name) {init(null, target, name, 0);}     //省略其余关键性代码......}

这样Demo2就不是线程类而是作为一个线程工作存在,线程工作就是线程所要执行的性能

咱们把new Demo2传进去就能够了,这样就传进去了一个线程工作

class Demo2 implements  Runnable{    @Override    public void run() {        System.out.println("线程执行起来了.....");    }    public static void main(String[] args) {        Thread thread =new Thread(new Demo2());        thread.start();    }}运行后果如下:线程执行起来了.....

论断:Runnable接口仅仅是作为一个线程工作,交给咱们的线程来执行

在Java中始终都在推崇面向接口编程,实现Runnable接口的形式,就是线程和工作进行拆散,这样其实让代码更解耦。

二、应用匿名外部类的形式创立线程


比如说咱们的这个线程就执行一次,那咱们就没有必要再去创立一个类,实现Runnable接口,而后再重写run办法

咱们能够采纳匿名的外部类,实现线程的创立操作,一样能够运行起来

public static void main(String[] args) {    new Thread(){        @Override        public void run() {            System.out.println("线程执行起来了.....");        }    }.start();}//运行后果如下:线程执行起来了.....

如果一个线程只须要执行一次的话,这样写反而会更加简便。

线程工作也能够通过Thread的构造方法传进去

public static void main(String[] args) {    new Thread(new Runnable() {        @Override        public void run() {            System.out.println("线程执行起来了.....");        }    }).start();}//运行后果如下:线程执行起来了.....

如果咱们如果把这两种形式写在一起,会输入什么呢?

public static void main(String[] args) {    new Thread(new Runnable() {        @Override        public void run() {            System.out.println("runnable.....");        }    }){        @Override        public void run() {            System.out.println("sub.....");        }    }.start();}//运行后果如下:sub.....

那么为什么会输入sub呢?咱们须要观看源码进行剖析

咱们在new Thread类的时候,对target进行初始化,把new Runable给到了类的target这个属性

public class Thread implements Runnable {        public Thread(Runnable target) {        init(null, target, "Thread-" + nextThreadNum(), 0);    }    //省略其余关键性代码......}

这时当咱们去调用start()办法启动线程之后,它就去找run()办法去了

public class Thread implements Runnable {        @Override    public void run() {        if (target != null) {            target.run();        }    }    //省略其余关键性代码......}

然而会执行父类的run办法吗?会执行target 属性的run办法吗?

那么依据咱们对Java根本语法的理解,咱们子类曾经笼罩父类的run()办法,那么将会执行的是子类的办法

那么咱们重写完后,它就曾经不再有target这么一回事了

不再和target玩了,所以这里无论你怎么写,必定是不会执行的。

三、带返回值的形式创立线程


后面二种形式咱们发现都是没有返回值的,而且没法往外抛出异样。

接下来咱们创立既有返回值又能抛出异样的线程,首先它须要实现Callable接口

@FunctionalInterfacepublic interface Callable<V> {    /**     * Computes a result, or throws an exception if unable to do so.     *     * @return computed result     * @throws Exception if unable to compute a result     */    V call() throws Exception;}

发现Callable外面只有一个call办法,call办法中用了泛型,call办法其实就相似于run()办法

v就是指定这个线程的返回值类型,比方咱们当初创立一个Demo3线程并指定返回一个Integer

class Demo3 implements Callable<Integer>{          @Override     public Integer call() throws Exception {            return null;     }}

接下来咱们对Demo3的call做一些事件,并看看是怎么回事

class Demo3 implements Callable<Integer>{    @Override    public Integer call() throws Exception {        System.out.println("正在计算60秒等于多少分钟.....");        Thread.sleep(10000);        return 1;    }}

这样线程工作就实现了当然只是仅仅是作为线程执行的工作。

接着咱们来创立这个线程对象,并看看怎么应用这个线程工作

public static void main(String[] args) {            Demo3 demo3 = new Demo3();            //接着咱们要应用FutureTask对它进行一个封装      FutureTask<Integer> task = new FutureTask<>(demo3);}

FutureTask 是对线程工作的一个封装,咱们一起来看看他的源码

public class FutureTask<V> implements RunnableFuture<V> {        //省略其余关键性代码......}public interface RunnableFuture<V> extends Runnable, Future<V> {    /**     * Sets this Future to the result of its computation     * unless it has been cancelled.     */    void run();}

咱们观看源码能够说FutureTask其实最终是实现了Runnable接口,所以咱们必定就能够把它包装到Thread类中去执行,接着就能够启动线程了

public static void main(String[] args) {    Demo3 demo3 = new Demo3();    //接着咱们要应用FutureTask对它进行一个封装    FutureTask<Integer> task = new FutureTask<>(demo3);    Thread thread = new Thread(task);    thread.start();}//运行后果如下:正在计算60秒等于多少分钟.....

启动了线程之后线程工作就开始执行了,咱们是不是要拿到返回后果啊,怎么拿返回后果呢?

public static void main(String[] args) {    Demo3 demo3 = new Demo3();    //接着咱们要应用FutureTask对它进行一个封装    FutureTask<Integer> task = new FutureTask<>(demo3);    Thread thread = new Thread(task);    thread.start();        Integer result = task.get();    System.out.println("线程最终执行后果为:"+result);}//运行后果如下:正在计算60秒等于多少分钟.....线程最终执行后果为:1

参考资料


龙果学院:并发编程原理与实战(叶子猿老师)