JAVA多线程之Thread VS Runnable

时间:2021-02-16 17:32:04
1,在java中开发线程主要有两种方式,一种是继承自Thread类,另一种是实现Runnable接口。

2,线程创建的两种方式:
a,继承Thread类:
class MyThread eatends Thread{
......
@Override
public void run(){
}
}
MyThread mt=new MyThread();
mt.start();

b,实现Runnable接口:
class MyThread implements Runnable{
......
@Override
public void run(){
}
}
MyThread mt=new MyThread();
Thread th=new Thread(mt);
td.start();

说明:在继承Thread类的main方法中,只需创建线程(MyThread mt=new MyThread()),

然后开启线程(mt.start())即可,但在实现Runnable接口的main方法中,还要向线程

(Thread th=new Thread(mt))中传递一个参数mt,mt为线程类(MyThread)实例化

的一个对象。


3,两种创建方式的比较:
a,Runnable方式可以避免Thread方式由于java单继承带来的缺陷,就是每次只能继承一个Thread。
b,Runnable的代码可以被多个线程(Runnable实例)共享,适合多个线程处理同一资源的情况。

4,关于这两种方式的区别,很难说明白,下面我用售票系统的代码来分析:

场景描述:假设春节来临,广州到北京的火车票还有5张,现在通过3个售票窗口来出售,这三个

售票窗口同时开放,随机的出售车票

代码实现:
a,首先通过继承Thread创建线程,来完成这项工作,然后分析结果,代码如下:
class MyThread extends Thread{
//一共有五张车票
private int TicketsCount=5;
//窗口,也就是线程的名字
private String name;

public MyThread(String name){
this.name=name;
}

@Override
public void run() {
while (TicketsCount>0) {
TicketsCount--;
System.out.println(name+"卖了一张票,剩余票数为:"+TicketsCount);
}

}

}


public class TicketsThread {
public static void main(String[] args) {
//创建三个线程,模拟三个售票窗口
MyThread mt1=new MyThread("窗口1");
MyThread mt2=new MyThread("窗口2");
MyThread mt3=new MyThread("窗口3");

//启动三个线程,开始卖票
mt1.start();
mt2.start();
mt3.start();
}
}

执行结果:
窗口1卖了一张票,剩余票数为:4
窗口2卖了一张票,剩余票数为:4
窗口1卖了一张票,剩余票数为:3
窗口2卖了一张票,剩余票数为:3
窗口1卖了一张票,剩余票数为:2
窗口2卖了一张票,剩余票数为:2
窗口1卖了一张票,剩余票数为:1
窗口2卖了一张票,剩余票数为:1
窗口3卖了一张票,剩余票数为:4
窗口3卖了一张票,剩余票数为:3
窗口1卖了一张票,剩余票数为:0
窗口3卖了一张票,剩余票数为:2
窗口3卖了一张票,剩余票数为:1
窗口3卖了一张票,剩余票数为:0
窗口2卖了一张票,剩余票数为:0

嗯?总共有5张票,现在3个售票窗口怎么卖出了15张票?仔细观察我们会发现,其实每个售票窗口

都卖出了5张票。这是怎么回事?其实这是java单继承所带来的缺陷,在创建线程时,3个窗口各自

创建了一个线程,每个线程都有自己的TicketsCount和name,各自执行了自己的run方法,所以在执

行程序时每个售票窗口都卖出了5张票。


b,通过实现Runnable接口的方法,来完成这项工作,代码如下:
class MyThread2 implements Runnable{
private int TicketsCount=5;
@Override
public void run() {
while (TicketsCount>0) {
TicketsCount--;

System.out.println(Thread.currentThread().getName()+"卖出了一张票,剩余票数为:

"+TicketsCount);

}
}
}

public class TicketsRunnable{

public static void main(String[] args) {
//实例化对象
MyThread2 mt=new MyThread2();
//创建三个线程,模拟三个售票窗口
Thread th1=new Thread(mt,"窗口1");
Thread th2=new Thread(mt,"窗口2");
Thread th3=new Thread(mt,"窗口3");
//启动三个线程,开始卖票
th1.start();
th2.start();
th3.start();
}
}
执行结果:
窗口1卖出了一张票,剩余票数为:4
窗口2卖出了一张票,剩余票数为:2
窗口2卖出了一张票,剩余票数为:0
窗口3卖出了一张票,剩余票数为:3
窗口1卖出了一张票,剩余票数为:1

我们会发现,通过实现Runnable接口的方法所得到的结果正是我们想要的。MyThread2是实现

Runnable接口的,先通过类实例化出对象(mt),然后再把对象作为参数传递给3个线程对象,

也就是说3个线程对象传递的是同一个Runnable对象,从而实现了资源(TicketsCount=5)共

享,所以三个售票窗口总共卖出了5张票。


通过这个例子,我们已经清楚了Runnable和Thread 的区别。还需要强调一点,无论是继承

Thread类,还是实现Runnable方法,都需要重写run()方法,然后在run()方法了实现要做的

情,最后通过start()方法启动线程。