文章标题 多线程中的Thread类与Runnable接口

来源:互联网 发布:pc蛋蛋单双算法 编辑:程序博客网 时间:2024/05/21 09:44

java中实现多线程有两种方式,一种是继承Thread这个父类,另一种是通过Runnable这个接口来实现,很明显前一种局限在一个类只能继承一个父类,但是却可以实现多个接口,这句话是不是很熟悉,嘿嘿。
废话先不说,先扔几个代码。
(-)Thread中的start( )与run( )方法的比较
第一种:线程a和线程b通过run()方法启动。

public class raT {    public static void main(String[] args) {        // TODO Auto-generated method stub          class MyThead extends  Thread{              private String name ;              public MyThead(String name) {                  super();                  this.name=name;              }              public void run() {                  for(int i=0;i<10;i++) {                      System.out.println(this.name+" i="+i);                  }              }          }          MyThead m1=new MyThead("线程a");          MyThead m2=new MyThead("线程b");          m1.run();          m2.run();    }}

see运行结果:
线程a i=0
线程a i=1
线程a i=2
线程a i=3
线程a i=4
线程a i=5
线程a i=6
线程a i=7
线程a i=8
线程a i=9
线程b i=0
线程b i=1
线程b i=2
线程b i=3
线程b i=4
线程b i=5
线程b i=6
线程b i=7
线程b i=8
线程b i=9
怎么样,是不是很整齐,程序的原理比较简单,就不说了这里。可以看出结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。
第二种:线程a和线程b通过start()方法启动。

public class raT2 {    public static void main(String[] args) {        // TODO Auto-generated method stub        class MyThead extends  Thread{          private String name ;          public MyThead(String name) {              super();              this.name=name;          }          public void run() {              for(int i=0;i<10;i++) {                  System.out.println(this.name+" i="+i);              }          }        }        MyThead m1=new MyThead("线程a");        MyThead m2=new MyThead("线程b");        m1.start();        m2.start();    }}

the result is:
线程a i=0
线程a i=1
线程a i=2
线程a i=3
线程b i=0
线程a i=4
线程a i=5
线程a i=6
线程a i=7
线程a i=8
线程a i=9
线程b i=1
线程b i=2
线程b i=3
线程b i=4
线程b i=5
线程b i=6
线程b i=7
线程b i=8
线程b i=9
很明显,这里两个线程是交替进行的,每次执行的结果几乎都不一样,跟os(操作系统)中多个事务通过时间片轮转的方式交替运行的原理挺相似的,一个事务的时间片用完了,就释放CPU让给其它事务,然后等到下一个时间片才能继续执行。至于这里为什么线程a和b为什么不是交替的,我猜它可能是a线程执行完了一个时间片,b还没有准备就绪,就继续运行a,或者相反。
第三种 线程a运行start()方法,线程b运行run()方法。
先看代码:

public class raT3 {    public static void main(String[] args) {        // TODO Auto-generated method stub        class MyThead extends  Thread{          private String name ;          public MyThead(String name) {              super();              this.name=name;          }          public void run() {              for(int i=0;i<10;i++) {                  System.out.println(this.name+" i="+i);              }          }        }        MyThead m1=new MyThead("线程a");        MyThead m2=new MyThead("线程b");        m1.start();        m2.run();    }}

运行结果为:
线程b i=0
线程a i=0
线程b i=1
线程a i=1
线程b i=2
线程a i=2
线程b i=3
线程a i=3
线程b i=4
线程a i=4
线程b i=5
线程a i=5
线程b i=6
线程b i=7
线程b i=8
线程b i=9
线程a i=6
线程a i=7
线程a i=8
线程a i=9
同样的,跟第二次一样这里每次运行,看到的结果跟前一次相同的可能性very小,线程a通过时间片轮转的方式,线程b一心想独占CPU,然后告诉a你得等我先执行完整个程序,a对b的霸权行为很是不满,就告诉了cpu,为了公平起见,cpu对它俩说就按程序计数器指示的地址来执行吧,显然程序顺序执行的话,线程a占绝对优先权,最终跟大家想的一样,a威风凛凛的打败了b,然后就像第二种方式一样,程序交替的运行。
没错,还有第四种方式,小伙伴们可以猜一下结果,猜对有奖!
第四种: 线程a执行run()方法,线程b执行start()方法。

public class raT4 {    public static void main(String[] args) {        // TODO Auto-generated method stub        class MyThead extends  Thread{          private String name ;          public MyThead(String name) {              super();              this.name=name;          }          public void run() {              for(int i=0;i<10;i++) {                  System.out.println(this.name+" i="+i);              }          }        }        MyThead m1=new MyThead("线程a");        MyThead m2=new MyThead("线程b");        m1.run();        m2.start();    }}

这里贴出运行结果:
线程a i=0
线程a i=1
线程a i=2
线程a i=3
线程a i=4
线程a i=5
线程a i=6
线程a i=7
线程a i=8
线程a i=9
线程b i=0
线程b i=1
线程b i=2
线程b i=3
线程b i=4
线程b i=5
线程b i=6
线程b i=7
线程b i=8
线程b i=9
没错,这里跟第一次一样,你运行100次这个结果也不会变,原理大家可以自己想一下哦,不介意你参考我第三种方法下的解题逻辑,哈哈哈。
是不是觉得很有意思,这里我们来个小小的总结吧:对start()和run()的关系,这里打个比方:

相当于玩游戏机,只有一个游戏机(cpu),可是有很多人要玩,于是,start是排队!等CPU选中你就是轮到你,你就run(),当CPU的运行的时间片执行完,这个线程就继续排队,等待下一次的run()。

相信大家也能看出来,多个线程之间只有调用start()方法才能真正做到多线程的并发执行;而run()方法偏霸道一点,它必须等一个线程运行完毕后才能执行下一个,这样CPU的利用率显然相对来说比较低。
(=)通过Runnable接口实现多线程的并发
老规矩,先上代码:

 public class raT5 {    public static void main(String[] args) {        // TODO Auto-generated method stub          class Mythread implements Runnable{              private String name;              public Mythread(String name) {                  this.name=name;              }            @Override            public void run() {                // TODO Auto-generated method stub                for(int i=0;i<10;i++) {                    System.out.println(this.name+" i="+i);                }            }          }          Mythread m1=new Mythread("线程a");          Mythread m2=new Mythread("线程b");         new Thread(m1).start();         new Thread(m2).start();    }}

运行结果如下:
线程a i=0
线程b i=0
线程a i=1
线程a i=2
线程a i=3
线程a i=4
线程a i=5
线程a i=6
线程a i=7
线程b i=1
线程a i=8
线程a i=9
线程b i=2
线程b i=3
线程b i=4
线程b i=5
线程b i=6
线程b i=7
线程b i=8
线程b i=9
感觉跟两个start()方法运行的结果差不多哈,其实看下面:
public class Thread extends Object implements Runnable
发现Thread类也是Runnable接口的子类,没什么用额,但是Runnable定义的子类中没有start()方法,只有Thread类中才有。也就是说可以通过Thread类的start()方法来启动Runnable实现多线程。另外,在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成,比如一些订票系统的并发处理,具体的例子想深入的同学可以自行搜索或者有大牛愿意的话可以写一个与并发处理相关的系统,不介意的话可以在评论中贴出网址供大家参考,目前我也没找到比较好的资源,这里就不贴了哈。