上一篇文章有应用到 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 接口
@FunctionalInterface
public 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
参考资料
龙果学院:并发编程原理与实战(叶子猿老师)