Java中启动线程start和run⽅法
⼀、区别
Java中启动线程有两种⽅法,继承Thread类和实现Runnable接⼝,由于Java⽆法实现多重继承,所以⼀般通过实现Runnable接⼝来创建线程。但是⽆论哪种⽅法都可以通过start()和run()⽅法来启动线程,下⾯就来介绍⼀下他们的区别。
start⽅法:
通过该⽅法启动线程的同时也创建了⼀个线程,真正实现了多线程。⽆需等待run()⽅法中的代码执⾏完毕,就可以接着执⾏下⾯的代码。此时start()的这个线程处于就绪状态,当得到CPU的时间⽚后就会执⾏其中的run()⽅法。这个run()⽅法包含了要执⾏的这个线程的内容,run()⽅法运⾏结束,此线程也就终⽌了。
run⽅法:
通过run⽅法启动线程其实就是调⽤⼀个类中的⽅法,当作普通的⽅法的⽅式调⽤。并没有创建⼀个线程,程序中依旧只有⼀个主线程,必须等到run()⽅法⾥⾯的代码执⾏完毕,才会继续执⾏下⾯的代码,这样就没有达到写线程的⽬的。
下⾯我们通过⼀个很经典的题⽬来理解⼀下:
public class Test {
public static void main(String[] args) {
Thread t = new Thread(){
public void run() {
pong();
}
};
t.run();
System.out.println("ping");
}
static void pong() {
System.out.println("pong");
}
}
代码如图所⽰,那么运⾏程序,输出的应该是什么呢?没错,输出的是”pong ping”。因为t.run()实际上就是等待执⾏new Thread⾥⾯的run()⽅法调⽤pong()完毕后,再继续打印”ping”。它不是真正的线程。
⽽如果我们将t.run();修改为t.start();那么,结果很明显就是”ping pong”,因为当执⾏到此处,创建了⼀个新的线程t并处于就绪状态,代码继续执⾏,打印出”ping”。此时,执⾏完毕。线程t得到CPU的时间⽚,开始执⾏,调⽤pong()⽅法打印出”pong”。
如果感兴趣,还可以多加⼏条语句⾃⼰看看效果。
⼆、源码
那么他们本质上的区别在哪⾥,我们来看⼀下源码:
/
**java
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception  IllegalThreadStateException  if the thread was already
*              started.
* @see        #run()
* @see        #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
begin和start区别it will be passed up the call stack */
}
}
}
private native void start0();
可以看到,当⼀个线程启动的时候,它的状态(threadStatus)被设置为0(即线程为NEW状态),如果不为0,则抛出IllegalThreadStateException异常。正常的话,将该线程加⼊线程组,最后尝试调⽤start0⽅法,⽽start0⽅法是私有的native⽅法(Native Method是⼀个java调⽤⾮java代码的接⼝)。
start调⽤后,线程出于就绪状态RUNNABLE,等分配到cpu时间⽚时系统会调⽤thread的run()⽅法,⽽这个⽅法其实只是调⽤runnable ⾥⾯⾃⼰实现的run()⽅法。
我们再看看Thread⾥run()的源码:
@Override
public void run() {
if (target != null) {
target.run();
}
}
如果target不为空,则调⽤target的run()⽅法。那么target是什么?其实就是⼀个实现Runnable接⼝的对象实例,正如上⾯代码中new Thread的部分,其实我们就是在实现它的run()⽅法。所以如果直接调⽤run,就和调⽤实例的⼀个普通⽅法没什么区别,是不会创建新的线程的,因为压根就没执⾏start0⽅法。
三、实现
前⾯说了,继承Thread类和实现Runnable接⼝都可以定义⼀个线程,那么他们⼜有什么区别呢?
在《Java核⼼技术卷1 第9版》第627页提到。可以通过⼀下代码构建Thread的⼦类定义⼀个线程:
class MyThread extends Thread {
public void run() {
//do Something
}
}
然后,实例化⼀个对象,调⽤其start⽅法。不过这个⽅法不推荐。应该减少需要并⾏运⾏的任务数量。如果任务很多,要为每个任务创建⼀个独⽴的线程所付出的代价太多,当然可以⽤线程池来解决。
实现Runnable接⼝所具有的优势:
1. 避免Java单继承的问题
2. 适合多线程处理同⼀资源
3. 代码可以被多线程共享,数据独⽴,很容易实现资源共享

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。