工作中或多或少都会用到多线程。调用start()方法而不是run()方法来启动多线程。为什么?在讨论这个问题之前,先来了解一下(复习一下)多线程的一些基础知识~线程状态Java中定义了6种线程状态,可以在Thread类中找到://为了节省篇幅,我删除了注释publicenumState{NEW,//初始状态RUNNABLE,//运行状态BLOCKED,//阻塞状态WAITING,//等待状态TIMED_WAITING,//超时等待状态TERMINATED;//终止状态}这6个状态之间的关系可以看下图:这张图还是很详细的,结合这张图,我们来说说这些状态是什么意思:1.NEW表示线程创建成功,但是没有运行。newThread之后,start之前,线程处于NEW状态;2.RUNNABLE表示线程正在运行。当我们运行strat方法,子线程创建成功后,子线程的状态变为RUNNABLE;3.TERMINATED表示线程已经运行完毕,子线程的状态会由RUNNABLE变为TERMINATED;4.BLOCKED表示线程被阻塞。如果线程只是在等待获取监视器锁,比如等待进入同步修改的代码块或方法时,就会由RUNNABLE变为BLOCKED;5、WAITING和TIMED_WAITING都表示等待。现在遇到Object#wait、Thread#join、LockSupport#park等方法时,该线程会等待另一个线程执行特定动作后才结束等待,但TIMED_WAITING有等待时间;优先级优先级表示线程执行机会的大小。优先级高的可以先执行,优先级低的可以后执行。Java源码中,优先级从低到高是1到10,默认新建线程的优先级是5。源码如下:/***Theminimumprioritythatthreadcanhave.*/publicfinalstaticintMIN_PRIORITY=1;/***分配给线程的默认优先级。*/publicfinalstaticintNORM_PRIORITY=5;/***线程可以拥有的最大优先级。*/publicfinalstaticintMAX_PRIORITY=10;创建线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。两种方法的使用方法如下:1.继承Thread,成为Thread的子类[]args){MyThreadthread=newMyThread();//启动线程thread.start();}}2.实现Runnable接口publicclassMyThread1{publicstaticvoidmain(String[]args){Threadthread=newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("我是通过runnable实现的~");}});//启动线程thread.start();}}无论使用哪种方法,启动线程都是thread.start()方法,如果你做过实验,你会发现thread.run()也是可以执行的,为什么要调用thread.start()方法呢?先说结论:首先可以通过object.run()方法方法执行,但不是多线程方法,是普通方法。要实现多线程方法,必须通过object.start()方法。如果你想理解一个问题,更好的方法是从源代码入手。我们也从这两个方法的源码入手。看一下start方法的源码:publicsynchronizedvoidstart(){/***Thismethodisnotinvokedforthemainmethodthreador"system"*groupthreadscreated/setupbytheVM.Anynewfunctionalityadded*tothismethodinthefuturemaytobeaddedtotheVM.**Azerostatusvaluecorrespondstostate"NEW"*///没有初化,抛出异常if(threadStatus!=0)thrownewIllegalThreadStateException();/*通知组这个线程即将启动*以便可以将其添加到组的线程列表*和组的未启动计数可以递减。*/组.add(this);//是否启动的标识符booleanstarted=false;try{//start0()是启动多线程的关键//这里会创建一个新线程,是一个native方法//执行完成后,新线程已经在运行start0();//主线程执行started=true;}finally{try{if(!started){group.threadStartFailed(this);}}catch(Throwableignore){/*donothing.Ifstart0threwaThrowablethenitwillbepassedupthecallstack*/}}}start方法的源码是o只有几行代码,注释更详细。最重要的是start0()方法,后面会解释。我们看一下run()方法的源码:@Overridepublicvoidrun(){//操作简单,没有新建线程,target为Runnableif(target!=null){target.run();}}run()method源码比较简单,就是一个普通方法的调用,也印证了我们上面的结论。接下来说说start0()方法,它是实现多线程的关键。start0()的代码如下:privatevoidstart0();start0被标记为native,是一个本地方法,我们不需要去实现或者理解,**为什么start0()被标记为native**?这个要从Java跨平台说起,看下图:start()方法调用start0()方法后,线程不一定马上启动Execution只是把线程变成可运行状态(NEW--->RUNNABLE).具体执行时间取决于CPU,由CPU统一调度。我们也知道Java是跨平台的,可以运行在不同的系统上。每个系统的CPU调度算法不同,需要区别对待。这件事只能由JVM来实现。start0()方法自然被标记为native。最后总结一下,Java中真正的多线程是start中的start0()方法,run()方法只是一个普通的方法。
