请选择 进入手机版 | 继续访问电脑版
本站特色:极好的技术研究氛围!所有技术发帖,必有回复! 做最受欢迎的Java论坛

疯狂Java联盟

 找回密码
 加入联盟
查看: 31|回复: 0

不要调用run方法——多线程的陷阱之一

[复制链接]
发表于 2017-12-7 16:39:12 | 显示全部楼层 |阅读模式
本帖最后由 kongyeeku 于 2017-12-7 16:47 编辑

本文节选自《疯狂Java程序员的基本修养》

Java语言提供了非常优秀的多线程支持,开发者能以简单的代码来创建、启动多线程。而且Java语言内置的多线程支持极好地简化了多线程编程。虽然如此,但Java多线程编程中依然存在一些容易混淆的陷阱。
从JDK 1.5开始,Java提供了3种方式来创建、启动多线程:
q     继承Thread类来创建线程类,重写run()方法作为线程执行体。
q     实现Runnable接口来创建线程类,重写run()方法作为线程执行体。
q     实现Callable接口来创建线程类,重写call()方法作为线程执行体。
对于这3种方式而言,第一种方式的效果最差,它有如下2点坏处:
q     线程类继承了Thread类,无法再继承其他父类。
q     因为每条线程都是一个Thread子类的实例,因此多个线程之间共享数据比较麻烦。
对于第2种和第3种方式而言,它们的本质是一样的,只是Callable接口的里包含的call()方法既可以声明抛出异常,也可以拥有返回值。
除此之外,如果采用继承Thread类的方式来创建多线程,程序还有一个潜在的危险,看如下程序:
程序清单:codes\05\5.8\InvokeRun.java
public class InvokeRun extends Thread
{
         privateint i ;
         //重写run方法,run方法的方法体就是线程执行体
         publicvoid run()
         {
                   for( ; i < 100 ; i++ )
                   {
                            //直接调用run方法时,Threadthis.getName返回的是该对象名字,
                            //而不是当前线程的名字。
                            //使用Thread.currentThread().getName()总是获取当前线程名字
                            System.out.println(Thread.currentThread().getName()+  " " + i);
                   }
         }        
    publicstatic void main(String[] args)
    {
        for(int i = 0; i < 100;  i++)
        {
                            //调用ThreadcurrentThread方法获取当前线程
                            System.out.println(Thread.currentThread().getName()+  " " + i);
                            if(i == 20)
                            {
                                     //直接调用线程对象的run方法,
                                     //系统会把线程对象当成普通对象,run方法当成普通方法,
                                     //所以下面两行代码并不会启动2条线程,而是依次执行2run方法
                                     newInvokeRun().run();
                                     newInvokeRun().run();
                            }
        }
    }
}
上面程序试图在主线程中i == 20时创建、并启动2条新线程,编译该程序,一切正常;运行该程序,我们发现该程序只有一个线程:main线程。程序执行的大致过程如下:
(1)当程序输出了main 20之后,又重新开始输出main 0;
(2)程序从main 0一直输出到main 99,再次从main 0开始输出;
(3)程序再次从main 0一直输出到main 99,再次从main 22开始输出,直到main 99结束。
上面程序始终只有一条线程,并没有启动任何新线程,关键是因为粗体字代码那里我们调用了线程对象的run()方法,而不是start()方法——启动线程应该使用start()方法,而不是run()方法。
如果程序从未调用线程对象的start()方法来启动它,那么这个线程对象将一直处于“新建”状态,它永远也不会作为线程获得执行的机会,它只是一个普通的Java对象。当程序调用线程对象的run()方法时,与调用普通Java对象的普通方法并无任何区别,因此绝对不会启动一条新线程。

您需要登录后才可以回帖 登录 | 加入联盟

本版积分规则

视频、代码、电子书下载
请关注"疯狂图书"公众号
QQ交流1群: 545923995  未满

小黑屋|手机版|Archiver|疯狂Java联盟 ( 粤ICP备11063141号 )

GMT+8, 2017-12-18 20:29 , Processed in 0.303312 second(s), 6 queries , File On.

快速回复 返回顶部 返回列表