导语:在java中实现多线程的方法有两种:继承Thread类和实现Runnable接口。在使用这两种方法创建多线程类时有什么不同呢?下面作简要分析。
一、继承Thread类法
1.1. 这里先给出一个小demo,实现代码如下:
1 | class MyThread extends Thread { |
运行结果(某次):
1 | 当前线程为:main |
从运行结果可以看出,开启了两个新线程Thread-0和Thread-1,两个线程互相抢占资源完成了打印操作,实现了多线程操作!
1.2. 为什么用start()启动线程而不是用run()方法?
因为线程的运行需要本地操作系统的支持,先分别来看看start()和run()方法的源码。
1 | /*start()方法源码*/ |
在上面代码中出现了关键字native,表明了start0()是一个本地方法,说明start0()方法的运行需要本地操作系统的支持。
1 | /*run()方法源码*/ |
(1)从上面两处代码可以看出:
- start()方法最后是由start0()方法完成,它会新运行一个线程,新线程会调用run()方法,且start()方法不能被重复调用[1],否则会抛出IllegalThreadStateException异常。
- run()方法同一般成员方法一样,可以被重复调用。单独调用run()的时不会启动新线程,而是继续在当前线程中执行run()。为了验证run()方法不会启动新线程,可以将1.小demo中的
mt_A.start();mt_B.start();改为mt_A.run();mt_B.runt();
运行结果(每次相同):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21当前线程为:main
main+线程A+运行了+1次
main+线程A+运行了+2次
main+线程A+运行了+3次
main+线程A+运行了+4次
main+线程A+运行了+5次
main+线程A+运行了+6次
main+线程A+运行了+7次
main+线程A+运行了+8次
main+线程A+运行了+9次
main+线程A+运行了+10次
main+线程B+运行了+1次
main+线程B+运行了+2次
main+线程B+运行了+3次
main+线程B+运行了+4次
main+线程B+运行了+5次
main+线程B+运行了+6次
main+线程B+运行了+7次
main+线程B+运行了+8次
main+线程B+运行了+9次
main+线程B+运行了+10次
从执行结果可以看出,上述字符串的打印都是在主线程中完成的,未开启新线程,并且打印是按照先A后B的顺序执行的!
二、实现Runnable接口法
2.1. 同样这里给出此方法实现的小demo,代码如下:
1 | class MyRunnable implements Runnable { |
运行结果如下:
1 | Thread-1+sell ticket:10 |
由结果可知使用Runnable接口的方式,完成了买票的业务。即用3个线程一块进行卖票,每张票只卖了一次,符合实际情况。
2.2. 用继承Thread类的方法执行卖业务时,就会发现行不通,具体情况如下:
1 | class MyRunnable extends Thread { |
运行结果如下
1 | Thread-0+sell ticket:10 |
由运行结果可知,此处的三个线程各卖了10张票,与实际情况不符。
三、小结
Thread类也实现了Runnable接口,应尽量使用实现Runnable的方式,因为实现Runnable接口的方式比继承Thread类方法多以下优势:
① 适合多个相同程序代码的线程去处理同一资源;
② 避免了Java单继承带来的局限性;
③ 增强了程序的健壮性,代码能被多个线程共享,代码与数据是独立的。
[1]:skywang12345. Java多线程系列–“基础篇”03之 Thread中start()和run()的区别