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 it will be passed up the call stack */ } } } private native void start0();
可以看到,当一个线程启动的时候,它的状态(threadstatus)被设置为0,如果不为0,则抛出illegalthreadstateexception异常。正常的话,将该线程加入线程组,最后尝试调用start0方法,而start0方法是私有的native方法(native method是一个java调用非java代码的接口)。
我猜测这里是用c实现的,看来调用系统底层还是要通过c语言。这也就是为什么start()方法可以实现多线程。而调用run()方法,其实只是调用runnable里面自己实现的run()方法。
我们再看看thread里run()的源码:
@override public void run() { if (target != null) { target.run(); } }
如果target不为空,则调用target的run()方法,那么target是什么:
/* what will be run. */ private runnable 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、代码可以被多线程共享,数据独立,很容易实现资源共享
总结一下:
1.start() 可以启动一个新线程,run()不能
2.start()不能被重复调用,run()可以
3.start()中的run代码可以不执行完就继续执行下面的代码,即进行了线程切换。直接调用run方法必须等待其代码全部执行完才能继续执行下面的代码。
4.start() 实现了多线程,run()没有实现多线程。
以上所述是小编给大家介绍的java中启动线程start和run方法,希望对大家有所帮助
推荐阅读
-
Java线程的start方法回调run方法的操作技巧
-
Java线程Run和Start的区别
-
Java~在使用isAlive时, 将线程对象已构造参数的形式传递给Thread对象时进行start启动时, 使用this和Thread.currentThread的差异
-
python中start和run方法的区别
-
j在多线程中java关键字synchronized的修饰静态方法和非静态方法得到的不同情况
-
j在多线程中java关键字synchronized的修饰静态方法和非静态方法得到的不同情况
-
java线程池之newFixedThreadPool(包括submit() 和 execute()两种方法的详解)
-
Java线程Run和Start的区别
-
Java中Thread.start()和Thread.run()之间的区别
-
Java中如何实现对象的序列化和反序列化?(两种方法)