Java-初步认识-第十三章-创建线程的第二种方法-实现Runnable接口

一. 引言

现在把之前的示例还原一下,创建线程的第一种方式。(继承创建子类对象,覆盖run方法)

之前的例子如上,存在着三个线程(目前自己能够理解认识的),主线程和自己创建的两个线程。cpu在这三者之间进行切换。

现在遇到一个瓶颈,Demo类当中有一部分代码,我们需要用到多线程执行,我们就继承了Thread类(什么时候用多线程)。

为什么要有多线程的出现,就为为了并发程序,提高效率。类中需要同时运行的代码,我们称之为线程任务,对其进行封装。

怎么封装呢?线程任务代码必须要在Demo类的,run方法当中。

一定要写成run()方法么?不一定,封装的代码可以在任何名称的方法里。为了能同时执行部分代码,我们要具备Thread类中的run方法,因此我们要继承Thread类,但是最终要在run()方法调用封装在某一些方法中的并发代码,实现多线程执行。

这带来的问题就是,想要执行多线程么,继承Thread类,叫爹。但如果Demo类有父类呢?java不允许多继承,怎么来解决这个问题。

有人提出,Fu类再继承Thread类。这么弄也行,这是不得已而为之。(这样显得体系都是线程体系,线性)

如果不是线程体系,就不要这么弄,一旦继承了,那么类的所属就确定了。你就属于线程的体系了,你就具备线程里面的所有功能(这里的线程和多线程不是一个东西)。

当一个类有自己的父类的时候,它就不能继承Thread类了。→讲到这里,相当于又重新开了一个分支,不是按着线性发展的路线来了。

二.

代码要并发执行,我们需要多线程来执行。那该怎么办?不继承,我们也必须有其他方案能解决。

现在就基于下面截图中的例子,来讲解。

现在的情况就是Demo类继承了Fu类,而且Demo类中还有需要多线程执行的封装代码块show(),但是又不能多继承Thread类,该怎么办?也就是说Demo类需要一个额外的功能,能够将内部的show代码块变成线程任务。以前是通过继承,调用来的,现在没有继承类和方法了。

现在需要去扩展Demo类的功能,它已经有了爹,再扩展就是接口。是不是我们要实现Demo类的功能扩展?对嘛,那就是接口。

接口就是用来实现扩展的,一个类在继承一个类的同时,是不是实现更多的接口,实现的目的是为了什么?是为了这个类增加更多的功能。→核心的意思就是,通过实现接口,来增加类的功能,具体怎么实现是各个类自己定义的。

现在要思考人家有没有给我们提供接口?(以前讲接口的时候,貌似都是自己定义的?)

如果没提供,我们就要重新想办法(整个思考的流程是这样的,多继承不让用,我们就想通过接口来间接实现,如果连接口都没有,那我们就再想其他办法)。

现在看到底有没有接口,打开API查阅。

三.

在对Thread类继承过程中,我们重点集中在Thread类中的两个方法上,finalize方法和run方法。

在对run方法的API查询中,截图如下,

介绍中交代了,有这么一个接口,叫做runnable。→这种路径找起来是比较方便的,因为我们要做的,是给Demo类增加能封装线程任务的功能。所以,我们要找和任务相关的接口或者方法。

点一下runable,查阅

runable本身就是java.lang包中的接口,runable单词含义是可运行的,

截图中的意思就是,如果你有一个类,想让线程去执行类中的一部分内容,这个类就要实现runable接口。要扩展其功能,让其具备封装任务的能力。

接下来具体演示怎么,不通过继承,采用接口的形式实现多线程。

runable接口非常简单,其中的方法只有一个,void run(),这个run就是用来封装线程任务的。

在Demo类中覆盖run方法,这个run方法来自于,实现runable接口而来。run方法里面的代码就是线程任务。

现在就已经完成线程任务的封装了。(实现runable接口,覆盖run方法)

接下来,研究主函数中的内容。询问创建的对象d1是不是线程对象,(说这句话是为了和原继承体系中的知识点进行比较),

答案:d1不是线程对象。Demo类有自己的父类,有自己的体系,Demo类没有跟Thread类叫爹,它怎么能跟Thread类呢?它根本就不是线程对象(在这里频繁地谈论线程对象,是不是为了讲述实现接口中的线程图解做铺垫呢?以区分于继承体系中的线程?)

同样的,d1不是线程对象,自然d1.start()也是无效的。是线程才能开启,d1都不是线程对象。这就意味着原有继承体系下的程序已经无法使用了。

在这里Thread类没有子类来创建对象,形成线程。那就自己出现来形成线程,t1就是线程对象。

按着这个思路,创建了线程对象,就开始启动线程,运行线程。

DOS结果显示,没有任何结果。这里线程运行的都是Thread类自己的run方法中的内容,不是我们想要的show方法中的内容,

它在执行完自己的线程内容后,就结束了。它的run方法做了什么事儿,我不知道,但是可以保证的是,绝对做的不是我们要的show内容。

我们创建线程,是为了运行我们自己的方法。

现在的问题就是,创建的t1线程对象和我们Demo类中的run线程任务有什么关系呢?我们现在要让线程对象开启后,调用Demo类中的run方法,这个方法调用一定要具备这个方法所属的对象,(一个方法被执行,通常是被对象所调用,也可以是类名调用,目前只有这两种),这个run方法所属的对象是谁,是Demo。我们将Demo类的对象和Thread类的对象t1联合起来,就有了运行的可能。

现在就看它俩怎么形成关系。

四.

Demo类对象可以调用run方法,它俩怎么构成联系呢?Demo类实现了runable接口。线程类是你要么继承我,覆盖我的run方法,如果你要是有了自己的父类,没办法继承我,你还可以去实现一个接口,是不是这个意思?那就意味着,线程在做的时候,它对外提供了规则。那么你认为,我们是实现规则的,它是不是应该是使用规则的?是吧,笔记本为了扩展功能,对外暴露了USB接口,笔记本是使用接口了,我们做了很多USB的设备是不是在实现接口。

因此,我们来看一下,我们在查阅Thread类的时候,线程对象它的特点在于,一建立,是不是要有执行的任务?是的,线程没任务,创建它干嘛?在调用start()之前,线程就要有任务。

以上面的截图来看,我们在t1.start()之前就要有任务,应该在Thread线程对象构造的时候,赋予任务。

线程对象一创建就应该有任务,然后我们开始调用start()开启。没任务,就晚了。

在Thread类的API介绍中,除了有空参数的构造函数,还有runable接口的构造函数。这就意味着,线程对象一建立,就有了runable接口对象进来。

而runable接口对象里面,就是封装的线程任务,

因此,直接将d传递到Thread类的构造函数中,(Demo类实现了接口,它的对象怎么就是接口对象了?)

视频中说d是runable的子类对象,

截图中说“其run方法被调用的对象”,在下面的截图中,run方法被调用,就是Demo类。

最后整个程序经过编译,运行是没有问题的。

上面所说的就是线程创建的,第二种方式。这里面包含了很多步骤。

针对第三点,也就是说你不传递,线程走的是自己的run方法,你传递,就走你传递的那个。

→依照视频中的讲解来看,实现runable接口的Demo类对象,具备了可以执行的线程任务,但是自己是开启不了的。

因此,通过创建线程对象,在创建的过程中,将Demo类对象传递给线程对象,其实传递的是封装的run方法。

附代码:

package test;
class Demo implements Runnable{
	
	public void run() {
		show();
	}
	
	public void show() {
		for(int x = 0;x<20;x++) {
			System.out.println(Thread.currentThread().getName()+".."+x);
		}
	}
}
class ThreadDemo {
	
	public static void main(String[] args)
	{
	Demo d = new Demo();
	Thread d1 = new Thread(d);
	Thread d2 = new Thread(d);
	
	d1.start();
	d2.start();
	}
}

猜你喜欢

转载自blog.csdn.net/fighting_future/article/details/80277035