上一讲中我们提到了继承Thread类是实现多线程的一种方式,那么现在就来看看第二种方式:Runnable接口。
废话少说,上代码(依旧是之前的例子):
public class ThreadDemoOne implements Runnable { int no,workno,sleeptime; public ThreadDemoOne(int no,int workno,int sleeptime) { // TODO Auto-generated constructor stub this.no = no; this.workno = workno; this.sleeptime = sleeptime; } @Override public void run() { // TODO Auto-generated method stub try { for(int i=0;i<5;i++){ System.out.println("Thread["+no+"] do work "+workno+"_"+i); Thread.sleep(1000); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { System.out.println("Main do work 1"); new Thread(new ThreadDemoOne(1,2,1000)).start(); System.out.println("Main do work 3"); Thread.sleep(1000); System.out.println("Main do work 4"); new Thread(new ThreadDemoOne(2,5,1000)).start(); Thread.sleep(1000); System.out.println("Main do work 6"); } }
乍一看貌似一样啊,请仔细看(红色代码部分),是的,区别就是这么小!
有人就有疑问了,oracle是不是吃饱了撑的(sorry,骂错了,当时还是SUN公司推出的多线程),为什么好端端要提供两种差不多的多线程实现方式呢?
不着急骂Java脑残,我们先来看看两者的关系:
首先,我们发现Thread是个类,而Runnable是接口,但凡计算机科班出身,或者参加过java面试的同学,都能轻而易举的说出类和接口的联系和区别,其中最重要的一点就是,类无法多重继承,而接口可以实现多个。好了,这就是Thread和Runnable的区别之一。
其次,我们发现Runnable接口方法中没有start()方法,而多线程激活需要这个方法,但这个方法存在于Thread类中。也就是可以说,对于实现Runnable接口的子类,也需要依靠Thread的start()方法来启动多线程。
最后,通过JDK文档,我们发现Thread类实现Runnable接口,即在本质上,Thread类是Runnable接口的子类。
了解了以上几点,相信大家都明白了Thread离不开Runnable,因为Runnable是他老子,而实现Runnable接口的子类也离不开Thread,因为要靠Thread来启动多线程,环环相扣,惺惺相惜,可昭日月~~
看完上面这段刻骨铭心的互相依赖关系,我们还是要写个观后感:建议读书者尽可能的使用Runnable接口去实现多线程机制,这样既可以避免了类的单继承局限,也可以使方法和启动解耦,而且也适合于资源共享!
等等,资源共享?上面没提到这个啊,这到底怎么回事?
对了,忘记说了,哈哈!我们还是来看代码吧,来个经典的12307-1关注的卖票问题,目前手头就五张票,要三个窗口一起卖,分别采用Thread和Runnable的方式:
public class SellTicket extends Thread { private int ticket = 5;//实际只有五张票 @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<100;i++){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+" sell ticket["+ticket--+"]"); } } } public static void main(String[] args) { //实例化了三个卖票对象,实际是各卖各的票,猜猜总共卖出去几张? new SellTicket().start(); new SellTicket().start(); new SellTicket().start(); } }
public class SellTicket implements Runnable { private int ticket = 5;//实际只有五张票 @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<100;i++){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+" sell ticket["+ticket--+"]"); } } } public static void main(String[] args) { //只实例化了一个卖票对象,实际是三个线程一起卖票,猜猜总共卖出去几张? SellTicket st = new SellTicket(); new Thread(st).start(); new Thread(st).start(); new Thread(st).start(); } }
这就是Runnable的共享资源的好处,今天就到这,洗洗睡了~