多线程的卖票示例来理解两种创建线程方法的区别
class Ticket implements Runnable{ private int num=100;//售票数一百张 public void run() { while(true) { if(num>0) { System.out.println(Thread.currentThread().getName()+"...."+num--); } } } } public class Ticket_Demo { public static void main(String[] args) { Ticket train=new Ticket();//火车票 Ticket HighSpeedRail=new Ticket();//高铁票 /** * 创建线程,运行的是同一对象a的run方法(同一任务,卖的是同一种票) */ Thread b=new Thread(train); Thread c=new Thread(train); Thread d=new Thread(train); Thread e=new Thread(HighSpeedRail); b.start(); c.start(); d.start(); e.start(); } }
此卖票程序的最终实现功能为:多个人卖同一张票
首先,我们看第一种:实现Runnable接口的方法
先解释一下程序,Ticket类实现Runnable接口,Runnable接口只有一个run方法(自行查阅API文档),run方法里面写的就是封装的任务代码---当while为true,循环不间断,设置了一个if条件判断:当票数>0,票数减少(否则会出现负数,这个与现实不符合),在main方法中创建一个Ticket类对象,这时候就能访问run()方法,但是此run方法并不是线程中的run方法,它只是子类覆盖(实现接口,又叫重写)的run方法,如果让train调用自身的run方法,不过是主线程开启(在一个程序中可以确认的两条线程为:主线程main和垃圾回收线程).
接着创建Thread线程对象,new Thread();[管理的是Thread类中的成员方法,把thread类比作一个学生的话,thread对象就是其中具体的一个人,比如小明,类拥有的成员方法可以理解为学生拥有的能力,比如学习,所以说对象new什么类就是获取其中的能力],此时b,c,d就拥有了Thread的start()[开启线程的能力,具体它开启哪个线程就看里面传的参数对象].这里我们有必要了解:以实现Runnable接口这种创建线程方法的运行细节和Thread类的一个构造方法
/** * 多线程实现细节 * * 实现Runnable接口的好处: * 1.将线程的任务从线程的子类中分离出来,进行了单独的封装[脱离了Thread体系] * 按照面向对象的思想将任务封装成对象 * 2.避免了java单继承的局限性 * * 所以,创建线程的第二种方式较为常用. * @author 罗摩衔那 * */ public class Implement_Detai { } class Thread { private Runnable r; Thread(){//第一种方法的行走路线 } Thread(Runnable r){//第二种方法的行走路线 this.r=r; } public void run() { r.run(); } public void start() { run();//启动线程 } }
所以说,Ticket_Demo程序中,Thread b=new Thread(train);中的train其实就是传进了一个Ticket对象作参数,开始我也有疑问不是传Runnable接口的对象进去嘛,怎么是其子类对象,这个我是这么理解的:其实我们java中的继承父类子类的关系就是更高级的抽象罢了,举个栗子:person是父类,人会说话,会呼吸(能力),student类继承了person类,student类会学习(能力),难道学生就不会呼吸,说话了嘛?这个传入其子类对象,也是合理的.传入后也就相当于获取到其子类的run方法了(结合第二段代码理解).
创建了几个thread对象不就像几个人在卖票,传入的是同一个train对象不就是执行的是同一个run方法(虽然开启了结果run方法,其实本质上是一个),这时候我们想卖不同的票,那就再创建一个Ticket对象.
最后看看继承thread方法的卖票程序
class Ticket extends thread{ private int num=100;//售票数一百张 public void run() { while(true) { if(num>0) { System.out.println(Thread.currentThread().getName()+"...."+num--); } } } } public class Ticket_Demo { public static void main(String[] args) { Ticket c=new Ticket(); Ticket d=new Ticket(); c.stat(); d.stat(); } }
就在与mian程序里的区别了,因为Ticket继承了Thread,所以就拥有了父类的start()开启线程的能力,不需要创建Thread对象[在体制内],可以这时候开启线程确是不同的run方法,也就是不同的num对象,你开启了几个线程,就创建了多少个一百张票惹~