A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 张_涛 黑马帝   /  2012-8-23 18:05  /  2917 人查看  /  12 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

  1. package cn.itcast.heima;

  2. public class TraditionalThread {
  3.         public static void main(String[] args) {
  4.                 new Thread(
  5.                                 new Runnable(){
  6.                                         public void run() {
  7.                                                 while(true){
  8.                                                         try {
  9.                                                                 Thread.sleep(500);
  10.                                                         } catch (InterruptedException e) {
  11.                                                                 e.printStackTrace();
  12.                                                         }
  13.                                                         System.out.println("runnable :" + Thread.currentThread().getName());

  14.                                                 }                                                       
  15.                                         }
  16.                                 }
  17.                 ){
  18.                         public void run() {
  19.                                 while(true){
  20.                                         try {
  21.                                                 Thread.sleep(500);
  22.                                         } catch (InterruptedException e) {
  23.                                                 e.printStackTrace();
  24.                                         }
  25.                                         System.out.println("thread :" + Thread.currentThread().getName());

  26.                                 }       
  27.                         }
  28.                 }.start();
  29.         }
  30. }
复制代码
Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么最后运行的是Thread子类覆盖的run方法中的代码。
这里,看源代码迷糊,求高手指导!

12 个回复

倒序浏览
我将楼主的程序改写成不使用匿名内部类的格式,但是代码是完全等价的。

先贴代码:
  1. package clone;

  2. public class DemoTest1
  3. {
  4.         public static void main(String[] args) {
  5.             Runnable runnable = new RunnableTest();
  6.             Thread thread = new ThreadTest(runnable);
  7.             thread.start();
  8.         }
  9. }

  10. class ThreadTest extends Thread
  11. {
  12.         <font color="red">private Runnable runnable;</font>          // 这行代码中的runnable在eclipse中被标为黄色警告

  13.     public ThreadTest(Runnable runnable) {
  14.             this.runnable = runnable;
  15.     }

  16.         public void run() {
  17.              
  18.         while(true){

  19.                 try {
  20.                         Thread.sleep(500);

  21.                 } catch (InterruptedException e) {
  22.                         e.printStackTrace();
  23.                 }

  24.                 System.out.println("thread :" + Thread.currentThread().getName());
  25.         }        
  26.     }
  27. }
  28. class RunnableTest implements Runnable
  29. {
  30.     public void run() {
  31.              
  32.         while(true){
  33.                 try {
  34.                         Thread.sleep(500);
  35.                 } catch (InterruptedException e) {
  36.                         e.printStackTrace();
  37.                 }

  38.                 System.out.println("runnable :" + Thread.currentThread().getName());
  39.         }                                                        
  40.     }
  41. }


  42.        
  43.        
复制代码
看到我写注释的那个地方不知楼主明白了没?
Thread的run方法是这样的:
public void run() {
if (target != null) {
     target.run();     // 如果传入了实现Runnable接口的类的对象,就直接调用它的run方法。
}
}
可是在你的程序中,继承自Thread的子类虽然传入了一个Runnable的参数,但是你完全没有使用它。以至于当我把它改写成不用匿名内部类的形式时,eclipse还会提示,runnable没有被使用到。

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1 赞一个!

查看全部评分

回复 使用道具 举报
黑马_许芸 发表于 2012-8-23 19:09
我将楼主的程序改写成不使用匿名内部类的格式,但是代码是完全等价的。

先贴代码:看到我写注释的那个地方 ...

是没有使用啊,可是为什么不使用呢?
Thread的run方法不是让重写了么?
回复 使用道具 举报
如果你写Thread t2 = new Thread(runnable); 它执行的一定是runnable的run方法。
这是因为Thread的run方法是这样定义的。
public void run() {
if (target != null) {
      target.run();     // 如果传入了实现Runnable接口的类的对象,就直接调用它的run方法。
}
}
也就是说,当这个Thread启动的时候,调用了这个Thread的run方法。然后它的run方法又调用了runnable的run方法。你懂了吗?
runnable的方法是通过Thread的run方法来调用的。
而你定义的Thread的子类的run方法中没有调用Runnable的run方法,所以,runnable的run方法根本不执行。

点评

说起来一开始,2楼就是正解呢。 ps。感叹一下,2楼真厉害啊,上次我来论坛还看到她发一个帖子说那天碰到一个有趣的新东西--匿名内部类。真是...  发表于 2012-8-24 14:23
回复 使用道具 举报
通过查看Thread类中的run方法的源代码:
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

其中,变量target是一个Runnable接口。若启动一个线程,首先检查是否传入实现Runnable接口的对象。如果没有传入,则直接调用子类重写的run方法。如果有传入,并且Thread的子类和实现Runnable接口的类同时覆写了run方法,这就要考虑执行顺序的问题,通过查看Thread类的源代码中的run方法,可知无论你是否传入实现Runnable接口的对象,都会首先调用Thread子类中覆写的run方法,如果此run方法没有调用传入的Runnable对象的run方法,就不会执行传入过来的的Runable对象的run方法。

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

回复 使用道具 举报
  1. private Runnable target;

  2. private int threadStatus = 0;


  3.         public synchronized void start() {
  4.         /**
  5.          * This method is not invoked for the main method thread or "system"
  6.          * group threads created/set up by the VM. Any new functionality added
  7.          * to this method in the future may have to also be added to the VM.
  8.          *
  9.          * A zero status value corresponds to state "NEW".
  10.          */
  11.         if (threadStatus != 0)
  12.             throw new IllegalThreadStateException();
  13.         group.add(this);
  14.         start0();
  15.         if (stopBeforeStart) {
  16.             stop0(throwableFromStop);
  17.         }
  18.     }

  19.     private native void start0();

  20.     /**
  21.      * If this thread was constructed using a separate
  22.      * <code>Runnable</code> run object, then that
  23.      * <code>Runnable</code> object's <code>run</code> method is called;
  24.      * otherwise, this method does nothing and returns.
  25.      * <p>
  26.      * Subclasses of <code>Thread</code> should override this method.
  27.      *
  28.      * @see     #start()
  29.      * @see     #stop()
  30.      * @see     #Thread(ThreadGroup, Runnable, String)
  31.      */
  32.     public void run() {
  33.         if (target != null) {
  34.             target.run();
  35.         }
  36.     }
复制代码
我说一下自己的理解,希望可以帮到楼主。

        首先Thread 启动一个线程 t 然后t.start()放法,先判断threadStatus != 0,因为
        新的线程Status都初始化为0,表示未被使用,是一个新线程,接着加入线程池 group.add(this);
        调用start0();这个方法是native的表示不是java语言实现的可能maybe是c语言实现的。
        start0()描述的很清楚If this thread was constructed using a separate
     * Runnable run object, then that
     * Runnable object's <code>run</code> method is called;
         就是说如果这个Thread是使用一个Runnable对象构造就运行Runable的run方法
         否则啥也不做返回。

         后面返回了,f (stopBeforeStart) {
            stop0(throwableFromStop);
        }是说如果线程还没开始就挺了就抛异常。

        我也有疑惑 没有调用本身的run方法啊?

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

回复 使用道具 举报
我觉得应该这样理解:
即使传入了Runnable对象,或者使用其他构造函数,但是Thread子类对象已经复写了run方法并创建了对象,那么运行后Thread的run方法就会被Thread子类run方法覆盖执行。
回复 使用道具 举报
本帖最后由 zhaosenyang 于 2015-12-23 10:26 编辑

Thread类run方法源代码:
public void run() {
        if (target != null) {
            target.run();
        }
    }

target是通过构造函数传入的一个Runnable实现类,你传入了一个Runnable的匿名实现类。如果你不覆写Thread的run方法,那通过源代码可见,start调用run方法的时候调用的是Runnable实现类的run方法。但是你又覆写了Tread的run方法,  
if (target != null)
{           
         target.run();
}

这句代码没有了,所以start调用Tread的run方法的时候就不会调用Runnable的run方法,而是调用你覆写的run方法。

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

回复 使用道具 举报
张_涛 黑马帝 2012-8-24 08:34:58
9#
吼吼,表示真心没看懂...
早上又看了下,这么理解了。
在创建匿名内部类的时候,调用的是无参数的Thread的构造方法
关于Thread的构造方法如下:
  1.     public Thread() {
  2.         init(null, null, "Thread-" + nextThreadNum(), 0);
  3.     }
  4.     public Thread(Runnable target) {
  5.         init(null, target, "Thread-" + nextThreadNum(), 0);
  6.     }
复制代码
因此,在用匿名内部类创建Thread子类的时候,默认的调用的是无参的构造方法,最后Runnable没有传进去。
吼吼!

点评

不是。Runnable传进去了。看我12楼的代码。  发表于 2012-8-24 14:31
回复 使用道具 举报
这个问题我的理解是:向Thread传进这个实现了run方法的Runnable对象,则运行Runnable对象的run方法,而后如果在方法体中又实现了Thead子类(匿名内部类)的run方法,则会覆盖Runnable的run方法(可以先将{}内的run方法注释掉来验证)。
因为Thread类本身就是实现了Runnable的,第一次,传进实现了run方法的Runnable对象会覆盖掉Thread类原本在实现Runnable时的run方法,第二次,当thread的子类(匿名内部类)实现run方法时,又会覆盖父类的run方法。
回复 使用道具 举报
张_涛 黑马帝 2012-8-24 11:59:33
11#
黑马张涛 发表于 2012-8-24 11:57
这个问题我的理解是:向Thread传进这个实现了run方法的Runnable对象,则运行Runnable对象的run方法,而后如 ...

Oh,no...
不是这样的。
看了张孝祥老师的多线程知道,起码不是你说的后面的run覆盖前面的run..
回复 使用道具 举报
哥们,简单问题想多了。
  1. package com.itheima.test;

  2. public class TraditionalThread {
  3.         public static void main(String[] args) {
  4.                 new Thread(new Runnable() {
  5.                         public void run() {//----------------------------------------1------------
  6.                                 while (true) {
  7.                                         try {
  8.                                                 Thread.sleep(500);
  9.                                         } catch (InterruptedException e) {
  10.                                                 e.printStackTrace();
  11.                                         }
  12.                                         System.out.println("runnable :"
  13.                                                         + Thread.currentThread().getName());

  14.                                 }
  15.                         }
  16.                 }) {
  17.                         public void run() {//----------------------------------------2---------
  18.                                 while (true) {
  19.                                         try {
  20.                                                 Thread.sleep(500);
  21.                                         } catch (InterruptedException e) {
  22.                                                 e.printStackTrace();
  23.                                         }
  24.                                         System.out.println("thread :"
  25.                                                         + Thread.currentThread().getName());

  26.                                 }
  27.                         }
  28.                 }.start();//----------------------------------------------------3---------
  29.         }
  30. }
复制代码
代码中,-------3---------处star()启动的是thread匿名子类-------------2-------------处run()线程,运行结果是thread :Thread-0 .....

回头看看你帖子说的那句话:在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么最后运行的是Thread子类覆盖的run方法中的代码


深究一下,为什么不调用-----------1-------------处的run()方法,如果你的Thread匿名子类不覆盖run方法,那么Thread的源码是调用target的run方法,也就是Runnable类或者其子类的run方法
  1. public void run() {
  2.         if (target != null) {
  3.             target.run();
  4.         }
  5.     }

复制代码

评分

参与人数 1技术分 +1 收起 理由
张_涛 + 1

查看全部评分

回复 使用道具 举报
本帖最后由 寇龙飞 于 2012-8-24 14:38 编辑

Runnable也传进去了
  1. package com.itheima.test;

  2. public class TraditionalThread {
  3.         public static void main(String[] args) {
  4.                 new Thread(new Runnable() {
  5.                         public void run() {
  6.                                 while (true) {
  7.                                         try {
  8.                                                 Thread.sleep(500);
  9.                                         } catch (InterruptedException e) {
  10.                                                 e.printStackTrace();
  11.                                         }
  12.                                         System.out.println("runnable :"
  13.                                                         + Thread.currentThread().getName());

  14.                                 }
  15.                         }
  16.                 }) {
  17.                         /*public void run() {
  18.                                 while (true) {
  19.                                         try {
  20.                                                 Thread.sleep(500);
  21.                                         } catch (InterruptedException e) {
  22.                                                 e.printStackTrace();
  23.                                         }
  24.                                         System.out.println("thread :"
  25.                                                         + Thread.currentThread().getName());

  26.                                 }
  27.                         }*/
  28.                 }.start();
  29.         }
  30. }

复制代码
注释掉thread匿名子类的run方法,那么thread自己的run方法会调用target.run(),target是Runnable的匿名子类,运行结果:
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
runnable :Thread-0
.
.
.
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马