文章目录
- 一、对比两种方式
-
- (1)对比
- (2)Runnable的好处
-
- 1、案例1
- 2、案例2
- (3)区别和联系
- (4)补充说明
- 二、笔试题
-
- (1)题1
- (2)题2
一、对比两种方式
(1)对比
<1> 两种方式步骤如下:
方式一:继承Thread
类
方式二:实现Runnable
接口
<2> 演示代码:计算大于某一规定值的质数的线程
①方式一:继承Thread类
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。
class PrimeThread extends Thread {
//1.创建类PrimeThread继承Thread
long minPrime; //属性
PrimeThread(long minPrime) {
//给指定数赋值
this.minPrime = minPrime;
}
public void run() {
//2.重写run方法
// compute primes larger than minPrime
. . .
}
}
--------------------------------------------------------------------------------
然后,下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143); //3.创建当前类PrimeThread的对象
p.start(); //4.用对象去调用start()方法,调用start()方法的时候,实际上调用的是run方法,run方法里面写的是这个线程要做什么事情
②方式二:实现Runnable接口
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
class PrimeRun implements Runnable {
//1、声明类PrimeRun实现Runnable接口
long minPrime;
PrimeRun(long minPrime) {
//通过构造器赋值指定数
this.minPrime = minPrime;
}
public void run() {
//2、计算比指定数大的质数都有哪些(逻辑代码)
// compute primes larger than minPrime
. . .
}
}
//--------------------------------------------------------------------------------
//然后,下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143); //3、创建实现类的对象
new Thread(p).start(); //4、把对象作为参数传递到Thread类的构造器中,造一个线程的对象,调用start()方法
🍰对比两种方式
- 共同点:
- ① 启动线程,使用的都是Thread类中定义的
start()
。 - ② 创建的线程对象,都是Thread类(方式二)或其子类(方式一)的实例。
- 方式一覆盖的是
Thread
里面的run()
方法,方式二覆盖的是Runnable
里面的run()
方法。
- ① 启动线程,使用的都是Thread类中定义的
- 不同点:
- 一个是类的继承,一个是接口的实现。
(2)Runnable的好处
☕对于Runnable
建议:建议使用实现Runnable接口的方式。
🍰实现Runnable接口比继承Thread类所具有的优势(Runnable
方式的好处):
① 实现的方式,避免的类的单继承的局限性。
② 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。更适合处理有共享数据的问题。(见下面案例)
③ 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。实现了代码和数据的分离。
比如上面的例子中,p可以作为一个共享数据传递到各个线程当中,将共享的数据封装到p里面,如下:
具体调用start()方法,start()调run(),就是具体要处理的代码逻辑,所以代码逻辑和数据分离了,如下:
现在的逻辑很简单,以后的数据会放到相应的数据库当中,或者在程序当中会以配置文件的方式去存放一些数据。
代码就是相应的逻辑,可以去读配置文件中的数据,然后在程序当中去运行。
配置文件中的数据后面还可以修改,程序不用变,还是去读文件。
1、案例1
比如前面举过的一个例子:
public class EvenNumberTest2 {
public static void main(String[] args) {
//3.创建当前实现类的对象
EvenNumberPrint p=new EvenNumberPrint();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
Thread t1=new Thread(p);
//5.通过Thread类的实例调用start()
t1.start();
//main()方法对应的主线程执行的操作
for (int i = 1; i <= 100; i++) {
if (i%2==0) {
System.out.println(Thread.currentThread().getName() + ":" + i); //方便查看线程
}
}
/*
* 再创建一个线程,用于遍历100以内的偶数
* */
Thread t2=new Thread(p);
t2.start();
}
}
//1.创建一个实现Runnable接口的类(实现类)
class EvenNumberPrint implements Runnable {
//2.实现接口中的抽象方法run()方法 -->将此线程要执行的操作,声明在此方法体中
@Override
public void run() {
//创建分线程遍历100以内的偶数
for (int i = 1; i <= 100; i++) {
if (i%2==0) {
System.out.println(Thread.currentThread().getName() + ":" + i); //方便查看线程
}
}
}
}
这里又创建了一个线程,让它遍历100以内的偶数。就是t2。
可以发现,我们并没有再去创建一个实现类的对象,虽然是两个线程,但是用的是同一个p。如下:
这个p就可以看作是这两个线程的共享数据。因为此时这两个线程共用这一个p。
2、案例2
①方式二实现Runnable接口
里面
现在类EvenNumberPrint
实现了接口Runnable
,假设现在有一个double类型的money
,如下代码:
package yuyi01.thread;
public class EvenNumberTest2