java线程之start方法与run方法解析以及currentThread与this解析
- start方法与run方法
currentThread与this
提示
博主:来自火星的萨满_906285288
博客地址:http://blog.csdn.net/qq_29924041
start方法与run方法解析
众所周知线程一般情况下都是通过start方法来进行执行的,只有调用了start方法之后,才会生成一个线程,在这里去源码里面查看一下start方法到底走了哪一步
/**
* 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 */
}
}
}
这里我们可以看到解释部分,调用此方法的时候,会调用一个start0方法,这是一个native方法,native方法也就是jni接口,在方法头部分解释的很清楚,调用此方法的时候会让java虚拟机调用这个线程的run方法,如果我没有猜错的话,虚拟机在底层同样是调用了类似c中的pthread这样一个东西才能得以创建线程。但是在这个方法里面有一点需要注意的地方,就是在start方法里面其实是有状态标志位进行判断的,如果进行了多次start方法之后,会抛出异常。
下面在展示下run方法:
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
run方法就比较简单,只是对一个target的判空和调用的过程,这个target事什么呢?
/* What will be run. */
private Runnable target;
在前面有一个局部变量,target原来是一个接口类型,这个Runable接口中有且只有一个方法,也就是run方法,在前面关于线程构造函数的博客中可以看到,线程在调用的时候可以通过构造参数讲Runable传进去,所以才会有在run方法中调用Runable的run方法。
看到这里就很明显了,run方法其实就是普通的类方法调用。
那么问题来了,如果new Thread().start();和new Thread().run();是都在主线程中还是一个是在主线程中,一个在子线程中???
简单测试一下
package com.zzf.java.charpter1.part3;
import java.util.Random;
public class MyThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for (int i = 0; i < 10; i++) {
int time = (int) (Math.random()*1000);
try {
sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t:"+i);
}
}
}
package com.zzf.java.charpter1.part3;
/**
*
* @author zhouzhangfei
* 在多线程中,既然封装了run方法,又封装了start方法,那么这两者之间是有什么区别和联系的呢??
* start方法是去启动异步的过程,实际上也会去调用run方法,只是异步
* 但是如果直接去调用run方法的话,这个时候就不是异步调用,也就是还是在主线程中去调用run方法
* 并且start的调用顺序其实并不是线程执行的调用顺序
*/
public class MyThreadTestMain_run {
public static void main(String[] args) {
MyThread myThread2 = new MyThread();
myThread2.setName("MyThread2");
myThread2.start();
MyThread myThread = new MyThread();
myThread.setName("MyThread");
myThread.run();
// for (int i = 0; i < 10; i++) {
// int time = (int) (Math.random()*1000);
// try {
// Thread.sleep(time);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"\t:"+i);
// }
}
}
输出的结果为:
MyThread2 :0
main :0
main :1
MyThread2 :1
MyThread2 :2
main :2
main :3
MyThread2 :3
main :4
MyThread2 :4
MyThread2 :5
main :5
main :6
MyThread2 :6
MyThread2 :7
main :7
main :8
MyThread2 :8
main :9
MyThread2 :9
可能会很奇怪,为毛线没有MyThread打印啊。MyThread调用的是run,而MyThread调用的是start。而调用了run方法的可以看到,其线程名字为main,因此它压根就没有创建子线程,仅仅是创建了一个对象,调用了一个普通的方法。
所以总结一下:
1:调用了start的方法的线程:start—->native层创建一个线程—->通过jni接口在去回调run方法以及其他相关的属性设置
2:调用了run的方法:run—–>主线程中去执行
所以千万要注意什么是线程,哪一个是线程。
currentThread与this解析
先上一段代码:
public class MyThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println(Thread.currentThread().getName());
}
}
这里的currentThead肯定是当前的线程,但是这个当前的线程指的是什么状态的当前???当前对象的线程??当前调用此方法的线程??
那在看一下下面这个方法:
public class MyThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println(this.getName());
}
}
有很多同学说,对!这两个方法在这里是一样的。那么我想问,在别的地方还会一样么???
不多谈!先来波概念:currentThread返回的是代码段正在被哪一个线程调用,而this指的是当前的线程的对象,所以要进行区分
currentThread指的是当前的这段代码段是被谁在调用,那么就是哪一个线程的,而this仅仅指的的当前的对象的,我们指的在线程创建的时候就是会默认去创建一个类似Thread-0这样的格式的名字的。this.getName指的就是这个名字
package com.zzf.java.charpter1.part9;
/**
*
* @author zhouzhangfei
*
*/
public class MyThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println(); //当前线程对象的名字,一般情况下,名字都是固定的,但是调用的对象就可以不一样
System.out.println("this.name:"+this.getName()+"\t"+"currentThread.getName:"+Thread.currentThread().getName()); //当前代码段被哪一个线程调用的名字
System.out.println("this.id:"+this.getId()+"\t"+"currentThread.getId:"+Thread.currentThread().getId());
}
}
package com.zzf.java.charpter1.part9;
/**
* 了解一下currentThread与this的区别
* currentThread返回的是代码段正在被哪一个线程调用,而this指的是当前的线程的对象,所以要进行区分
* @author zhouzhangfei
*
*/
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
System.out.println("=======");
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"A");
Thread thread2 = new Thread(myThread,"B");
Thread thread3 = new Thread(myThread,"C");
thread.start();
thread2.start();
thread3.start();
myThread.start();
}
}
输出的结果为:
main
=======
this.name:Thread-0 currentThread.getName:A
this.id:9 currentThread.getId:10
this.name:Thread-0 currentThread.getName:B
this.id:9 currentThread.getId:11
this.name:Thread-0 currentThread.getName:C
this.id:9 currentThread.getId:12
this.name:Thread-0 currentThread.getName:Thread-0
this.id:9 currentThread.getId:9
从上面的输出结果中可以看到,对于new出来的对象的话,这个时候其ID和name都是固定下来的为9和Thread-0;
但是如果它没有调用start方法的话,这个就时候就不能把它当做线程来看,只能当做一个普通的对象来看,它拥有一个很平凡的run方法,没有任何奇异之处。
而在后面new出了几个线程来调用这个myThread对象,这个时候currentThread很明显,对应的是其他的名字,ID对应的也是不同的,因此只有当自己的线程对象start方法调用的时候,currentThread与this才会指向同一个对象,如果被别的线程去调用的话,其currentThread与this不会去指向同一个线程对象。
注意:
使用的时候千万注意判别哦。很多时候搞不好就会出现错误的