叶子猿java并发编程_我所知道并发编程之使用Runnable、匿名内部类、带返回值的方式演示多种线程创建(一)...

上一篇文章有使用到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);

}

//省略其他关键性代码......

}

14854de32726f3802ec7558b06bb997e.png

这时当我们去调用start()方法启动线程之后,它就去找run()方法去了public class Thread implements Runnable {

@Override

public void run() {

if (target != null) {

target.run();

}

}

//省略其他关键性代码......

}

但是会执行父类的run方法吗?会执行target 属性的run方法吗?

那么根据我们对Java基本语法的了解,我们子类已经覆盖父类的run()方法,那么将会执行的是子类的方法

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

b06f7562b8f06f24a9aa584243430afe.png

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

42647a35d51e014a353edb06c9186771.png

三、带返回值的方式创建线程

前面二种方式我们发现都是没有返回值的,而且没法往外抛出异常。

接下来我们创建既有返回值又能抛出异常的线程,首先它需要实现Callable接口@FunctionalInterface

public interface Callable {

/**

* 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线程并指定返回一个Integerclass Demo3 implements Callable{

@Override

public Integer call() throws Exception {

return null;

}

}

接下来我们对Demo3的call做一些事情,并看看是怎么回事class Demo3 implements Callable{

@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 task = new FutureTask<>(demo3);

}

FutureTask 是对线程任务的一个封装,我们一起来看看他的源码public class FutureTask implements RunnableFuture {

//省略其他关键性代码......

}

public interface RunnableFuture extends Runnable, Future {

/**

* 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 task = new FutureTask<>(demo3);

Thread thread = new Thread(task);

thread.start();

}

//运行结果如下:

正在计算60秒等于多少分钟.....

启动了线程之后线程任务就开始执行了,我们是不是要拿到返回结果啊,怎么拿返回结果呢?public static void main(String[] args) {

Demo3 demo3 = new Demo3();

//接着我们要使用FutureTask对它进行一个封装

FutureTask task = new FutureTask<>(demo3);

Thread thread = new Thread(task);

thread.start();

Integer result = task.get();

System.out.println("线程最终执行结果为:"+result);

}

//运行结果如下:

正在计算60秒等于多少分钟.....

线程最终执行结果为:1

参考资料

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值