首先,从使用形式上来看,明显的使用Runnable实现多线程能够实现多继承,看起来会更好。不过,Thread和Runnable之间,本身也有一些联系。
首先关注一下Thread的定义:
public class Thread extends Object implements Runnable{
...
}
线程类的继承关系
仔细深挖Thread的定义,可以发现其实Thread本身也是对Runnable接口的实现。那么,Runnable的意义又在哪里呢?从下面的线程类继承结构中可以简单梳理一下。
线程类的继承结构
一般情况下,我们都是按照如下思路实现多线程的:
- 定义一个核心业务所在的执行线程
MyThread
,实现Runnable接口 - 当用户要调用该类的对象时,执行的机制是:使用
new Thread(new MyThread()).start()
方法进行启动。此时,后台的实现过程是:- 用户使用Thread类的start方法,调用JVM的start0方法实现对操作系统的资源调度。也就是说,Thread类祈祷的资源仅仅是沟通客户启动请求与JVM实际资源划分的过程。相当于是一个代理,代理用户的资源划分请求。实际执行的是MyThread的业务,这个过程中,实现了一个清晰的多线程模型。
一个典型的多线程数据共享模型
使用Runnable多线程实现的程序类,可以更好地描述出数据共享的概念(并不是说Thread不能)。
卖票模型是指通过多个线程共同消费同一个堆空间。注意参考下面的图片和代码。
卖票模型
class MySalerThread implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
for(int i=0;i<20;i++){
if (ticketNum > 0) {
System.out.println("ticket = "+ticketNum--);
}
}
}
}
public class TicketSaler {
public static void main(String args[]){
MySalerThread saler = new MySalerThread();
new Thread(saler).start();
new Thread(saler).start();
new Thread(saler).start();
}
}
执行结果:
卖票程序
Tips:区分下面这样的代码
public class TicketSaler {
public static void main(String args[]) {
// MySalerThread saler = new MySalerThread();
// new Thread(saler).start();
// new Thread(saler).start();
// new Thread(saler).start();
//区别下面的使用方法
new Thread(new MySalerThread()).start();
new Thread(new MySalerThread()).start();
new Thread(new MySalerThread()).start();
}
}
上面的使用方法,相当于开辟了三块内存,这样是不能实现数据共享的。