JAVA-初步认识-第十三章-多线程(第二种方式的好处)
一. 梳理两种线程创建方式
Runable接口的体现形式交代完了,现在说说为什么这么做,同时说说Runable接口干嘛用的,交代一下设计思想。
以上图为例来讲述,相当于规定了前提条件Student类有父类Person类。
现在Student类中有封装Code()代码块,想要让Code()这部分和Student类中的其它部分同时执行,这样就需要多线程。
如果Student类没有父类的话,就可以通过继承Thread类,获得Thread类中的所有数据,包括run()方法。Student类为了能够让
Code()代码块同步执行,Student类中必须创建run()方法,并且将Code()代码块放入run()方法体中覆盖父类Thread类中的run()方法。
当然在主函数中使用时,必须要新建Student类的对象,调用其start()方法,启动自己的run()方法,就实现了程序的并发执行。这就是第一种多线程创建方式。
现在图中Student类有自己的父类,由于java不允许多继承,只能通过实现Runable接口的形式来创造出多线程。
我们用类描述事物的时候,事物描述完了之后。如果仅仅是需要将一部分代码被多线程技术所操作的话,有必要具备线程对象中的所有方法吗?没有必要。其实,我们做继承的目的仅仅是为了覆盖run方法,建立我们自己要运行的任务。因此,就有了另外一种Runable方式。
(截图说倒是没有错,在后面的传递中,Thread类中确实定义了private Runable r,这就是执行内容被封装)
以前做这件事的时候,先要继承,先成为线程的子类,然后再去封装线程任务。现在是就将线程任务封装,而不需要线程的出现。(继承Thread类后,自己也就变成了线程对象,属于同一体系) 如果Student类中需要有执行的多线程代码的话,就先实现接口,把这个任务封装到run方法当中,就变成了Runable接口的子类对象。你是我的一个线程任务对象。
将任务封装完,我们如何运行这个任务呢?直接通过Thread类再创建对象,在创建对象的同时,去明确该线程的任务。
怎么创建呢?
Student类属于Runable接口的子类,因此Runable r=new Student()这句话没有问题,有点多态的意思。Thread t=new Thread();这句话就是创建了线程对象,可是这个线程对象没有我们的指定任务,它只有自己的默认任务。所以,在创建时要传递r,就是new Thread(r),这样一来,在初始化线程的时候,就明确了线程要执行的任务。任务本身在run方法当中的,run方法需要被对象调用,那么就把任务封装成对象。我把对象告诉线程,线程就执行这个对象去了。最后t.start()上运行就是我们指定的任务代码。
因此,Runable接口的出现,就是为了将线程的任务进行对象封装。
这是思想的变化,将线程任务单独封装成了对象。
以前继承Thread类的时候,我们是线程的子类,任务只是我们的对象中的一部分内容。现在是单独将线程的任务封装起来,类型就是Runable类型。
另外说个插曲,在看Thread类的时候,(下图中所示),Thread类既继承了object类,同时实现了Runable接口。
(可能Thread类也能将自己的任务抽离出来)
首先要明确,Runable是接口,类似于抽象类。Thread类是线程,里面有要运行的线程任务,如果Student类中也有自己要运行的线程任务的话,线程类和学生类有相同的东西,它们就可以进行抽取。可是他们抽取不出来父类,只能抽个接口。为什么呢?这个任务算是事物中的额外功能,我这个事物一定要线程运行吗?没有。我的理解是run方法这种创建多线程执行任务,主线程也可以操作,不一定非要通过多线程来执行,这属于额外功能。所以设计的时候,Thread类的run方法也来自Runable接口,只不过Thread类对run方法进行了默认实现。
将线程任务封装对象,这是一个思想的变化。
二. 两种方式的区别
实现Runable接口和继承Thread类的区别。实现Runable接口的一个好处,这个好处是继承Thread类没有的。