首页 > 代码库 > 探Java多线程Thread类和Runnable接口之间的联系
探Java多线程Thread类和Runnable接口之间的联系
首先复习一下Java多线程实现机制,Java实现多线程方法有如下这么几种:
1、继承了(extends)Thread类
2、实现了(implements)Runnable接口
也就是说 有如下两种情况
情况1: 继承Thread类。重写其方法run() . 然后new之、调用Start()方法
1 public class TestThread 2 { 3 private int i; 4 public static void main(String[] args) 5 { 6 // TODO 自动生成的方法存根 7 for(int i=0;i<50;i++) 8 { 9 System.out.println(Thread.currentThread().getName()+":"+i);10 if(i==20)11 {12 new newThread_1("thread__1").start();13 }14 }15 }16 17 }18 class newThread_1 extends Thread19 {20 public newThread_1(String threadName)21 {22 super(threadName);23 }24 public void run()25 {26 for(int i=0;i<50;i++)27 {28 System.out.println(Thread.currentThread().getName()+":"+i);29 }30 }31 }
方法2:实现Runnable接口 把实现接口的类new之 ,作为Thread的构造参数。
1 package com.chenjun.test; 2 3 4 public class TestThread 5 { 6 /** 7 * @param args 8 */ 9 public static void main(String[] args)10 {11 // TODO 自动生成的方法存根12 for(int i=0;i<50;i++)13 {14 System.out.println(Thread.currentThread().getName()+":"+i);15 if(i==20)16 {17 new Thread(new newThread()).start();18 }19 }20 }21 }22 23 class newThread implements Runnable24 {25 26 @Override27 public void run()28 {29 // TODO 自动生成的方法存根30 for(int i=0;i<50;i++)31 {32 System.out.println(Thread.currentThread().getName()+":"+i);33 }34 }35 36 }
那么问题来了。到底是继承父类Thread方便呢,还是实现Runnable接口好呢? 众所周知,Java的面向对象特性里面不支持多重继承,但是允许某类实现多个接口,这个问题的答案是不言而喻的 。 如果可以不继承父类,那么就应当尽量避免继承。实现接口是个良好的编程风格
本文中对基础知识的回顾就到底为止。 接下来,探讨一下在不阅读Java SDK的源码的情况下, 劳资带你一起来推测一下JDk的Thread类和Runnable究竟是个神马子关系.
首先:我们先谈点“题外话” Java之设计模式——代理模式
1,什么是代理模式?
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
2,策略模式有什么好处?
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
3,代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
4,应用场景举例:
比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时是这样的体现的
先说说这个场景中的要素:一种类型的女人,潘金莲,王婆,西门庆,后来扩展的贾氏也和西门庆勾上了,我们是假设的,然后西门庆找潘金莲happy,但潘金莲不好意思直接,就找个王婆代理呗。我们看看具体代码。
1 package com.yangguangfu.proxy; 2 /** 3 * 4 * @author 阿福(trygf521@126.com)<br> 5 *定义一种类型的女人,王婆和潘金莲都属于这个类型的女人 6 */ 7 public interface KindWoman { 8 9 //这种女人能做什么事情呢? 10 public void makeEyesWithMan();//抛媚眼 11 12 public void happyWithMan();//和男人那个.... 13 14 }
一种类型嘛,那肯定是接口,定义个潘金莲
1 package com.yangguangfu.proxy; 2 /** 3 * 4 * @author 阿福(trygf521@126.com)<br> 5 *定义一个潘金莲是什么样的人 6 */ 7 public class PanJinLian implements KindWoman{ 8 9 @Override 10 public void happyWithMan() { 11 System.out.println("潘金莲和男人在做那个..."); 12 13 } 14 15 @Override 16 public void makeEyesWithMan() { 17 System.out.println("潘金莲抛媚眼..."); 18 19 } 20 21 }
再定义个丑陋的王婆
1 package com.yangguangfu.proxy; 2 /** 3 * 4 * @author 阿福(trygf521@126.com)<br> 5 *王婆这个人老聪明了,她太老了,是个男人都看不上她, 6 *但是她有智慧经验呀,他作为一类女人的代理! 7 */ 8 public class WangPo implements KindWoman { 9 10 private KindWoman kindWoman; 11 12 public WangPo(){ 13 //默认的话是潘金莲的代理 14 this.kindWoman = new PanJinLian(); 15 } 16 //她可以是KindWomam的任何一个女人的代理,只要你是这一类型 17 public WangPo(KindWoman kindWoman){ 18 this.kindWoman = kindWoman; 19 } 20 21 @Override 22 public void happyWithMan() { 23 //自己老了,干不了了,但可以叫年轻的代替。 24 this.kindWoman.happyWithMan(); 25 26 } 27 28 @Override 29 public void makeEyesWithMan() { 30 //王婆年纪大了,谁看她抛媚眼啊 31 this.kindWoman.makeEyesWithMan(); 32 33 } 34 35 }
两个女主角都上场了,该男主角了,定义个西门庆
1 package com.yangguangfu.proxy; 2 /** 3 * 4 * @author 阿福(trygf521@126.com)<br> 5 *水浒传是这样写的:西门庆被潘金莲用竹竿敲了一下,西门庆看痴迷了,被王婆看到了,就开始撮合两人好事,王婆作为潘金莲的代理人收了不少好处费,那我们假设一下: 6 *如果没有王婆在中间牵线,这两个不要脸的能成事吗?难说得很! 7 */ 8 public class XiMenQiang { 9 10 /** 11 * @param args 12 */ 13 public static void main(String[] args) { 14 WangPo wangPo; 15 //把王婆叫出来 16 wangPo = new WangPo(); 17 //然后西门庆说,我要和潘金莲Happy,然后王婆就安排了西门庆丢筷子哪出戏: 18 wangPo.makeEyesWithMan(); 19 //看到没有表面是王婆在做,其实爽的是潘金莲 20 wangPo.happyWithMan(); 21 22 23 24 } 25 26 }
那这就是活生生的一个例子,通过代理人实现了某种目的,如果真去了王婆这个中间环节,直接西门庆和潘金莲勾搭,估计很难成就武松杀嫂事件。
那我们再考虑一下,水浒里面还有没有这类型的女人?有,卢俊义的老婆贾氏(就是和那个管家苟合的那个),这个名字起的:“贾氏”,那我们也让王婆做她的代理:
1 package com.yangguangfu.proxy; 2 /** 3 * 4 * @author 阿福(trygf521@126.com)<br> 5 *定义一个贾氏是什么样的人 6 */ 7 public class JiaShi implements KindWoman { 8 9 @Override 10 public void happyWithMan() { 11 System.out.println("贾氏和男人在做那个..."); 12 13 } 14 15 @Override 16 public void makeEyesWithMan() { 17 System.out.println("贾氏抛媚眼..."); 18 19 } 20 21 22 }
接下来,西门庆勾潘金莲又勾引贾氏:
1 package com.yangguangfu.proxy; 2 3 public class XiMenQiang { 4 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) { 9 WangPo wangPo; 10 //把王婆叫出来 11 wangPo = new WangPo(); 12 //然后西门庆说,我要和潘金莲Happy13 wangPo.makeEyesWithMan(); 14 //看到没有表面是王婆在做,其实爽的是潘金莲 15 wangPo.happyWithMan(); 16 17 18 19 //西门庆勾引贾氏 20 JiaShi jiaShi = new JiaShi(); 21 wangPo = new WangPo(jiaShi); 22 wangPo.makeEyesWithMan(); 23 wangPo.happyWithMan(); 24 25 } 26 27 }
说完这个故事,那我总结一下,代理模式主要使用了java的多态,干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚得很,同样一个接口呗。好了不多说了,慢慢体会吧。(注:此故事转载于http://yangguangfu.iteye.com/blog/815787博客)
好的,有点扯远了。 我们回来接着讨论Runnable和Thread类的关系问题。
听完这个很黄很暴力的故事,我想说,Thread类和Runnable接口两者之间有代理设计模式的模型在里面。信否? look! 看下面:
以下代码我写了很详细的注释,看完估计就对Thread和Runnable的关系知根知底了。比自己去阅读JDK代码好多了;
1 package com.chenjun.test; 2 3 public class Test 4 { 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) 9 {10 // TODO 自动生成的方法存根11 Demo1 demo1 = new Demo1();12 demo1.start();13 new MyThread(new MyRunnable() { //图个方便,用匿名内部类来试试 ,语法结构等价于 new Thread(new Runnable(){}).start();14 @Override15 public void run()16 {17 // TODO 自动生成的方法存根18 System.out.println("匿名Demo is call the run()!!!");19 }20 }).start();21 new MyThread(new Demo2()).start();22 }23 }24 interface MyRunnable25 {26 public void run();27 }28 class MyThread implements MyRunnable //这里的MyThread类充当了 ”任何一个实现MuRunnable接口的类“ 的代理 (侧重理解这句话! )29 {30 private MyRunnable my_runnable; //模拟Thread类里面藏了一个Runnable接口的引用。跟上面潘金莲例子 Kingwomen类是不是异曲同工 ?31 public MyThread(MyRunnable myRunnable)32 {33 // TODO 自动生成的构造函数存根34 this.my_runnable = myRunnable;35 }36 public MyThread() //默认无参构造37 {38 39 }40 public void run()41 {42 my_runnable.run(); //发生多态 ,父类引用直接调方法。等于调用其子类中实现的方法43 }44 45 public void start() //start方法体内部其实就是run! 因为我们再做多线程程序的时候重写的是run,而启动线程的时候确调用的start。start执行的仍旧是run()方法46 {47 run();48 }49 }50 51 class Demo1 extends MyThread //模拟实现Thread类实现多线程52 {53 @Override54 public void run()55 {56 System.out.println("Demo1 is call the run()!!!");57 }58 }59 60 class Demo2 implements MyRunnable //模拟实现Runnable接口类实现多线程61 {62 63 @Override64 public void run()65 {66 // TODO 自动生成的方法存根67 System.out.println("Demo2 is call the run()!!!");68 }69 70 }
运行结果:
Demo1 is call the run()!!!
匿名Demo is call the run()!!!
Demo2 is call the run()!!!
怎么样。是不是和Thread和Runnable接口的模型一模一样?
探Java多线程Thread类和Runnable接口之间的联系