Java并发编程(三)线程的基本使用

Thread类的基本使用(一)

线程的启动

线程的启动很简单,要知道所谓线程,其实也是属于Java的一个类而已,你可以通过继承这个类去使用它,也能够直接去使用它。

1
2
3
Thread t=new Thread();
t.start();
//这样线程就已经启动啦

不过这样的线程并没有任何实体,你可以尝试去启动这个线程,不过这个,这个线程并没有什么实体,已启动就结束了。因为这个线程并没有重写run方法,当然也可以在测试类继承thread类

1
public class test extends Thread

然后再重写run方法,发现……

1
2
3
4
5
6
7
8
9
10
public class test extends Thread{
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
System.out.println("xxx");
}
}.start();
}
}

打印出 xxx。这样就成功了??这又是什么个逻辑呢

为什么呢,我们点开thread类的源码,看看start方法是怎么回事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 */
}
}
}

好吧,这里有点长,我画一下重点

1
2
 start0();
//这个启动了一个新的线程

再看一下start0是什么

1
private native void start0();

我们发现,这个方法居然没有实体??不,不对,你仔细看看,它是一个native函数,这是什么,继续深挖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* openjdk\jdk\src\share\native\java\lang\Thread.c */

#include "jni.h"
#include "jvm.h"

#include "java_lang_Thread.h"

#define THD "Ljava/lang/Thread;"
#define OBJ "Ljava/lang/Object;"
#define STE "Ljava/lang/StackTraceElement;"
#define STR "Ljava/lang/String;"

#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))

static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

#undef THD
#undef OBJ
#undef STE
#undef STR

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

这居然是一个c++函数,我们可以看到我们几乎在thread类中的所有操作都有所涉及。

主要是这个 JVM_StartThread 方法,这又是什么呢,我们尝试着打开jvm.cpp文件,搜索一下JVM_StartThread ,发现它被一个叫做JVM_ENTRY给调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;

// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
MutexLocker mu(Threads_lock);
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);

if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}

if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}

Thread::start(native_thread);

JVM_END

其中有一行

1
native_thread = new JavaThread(&thread_entry, sz);

有native_thread,那这个JavaThread将一个thread_entry这个指针放入了构造函数中,看一看这个thread_entry

1
2
3
4
5
6
7
8
9
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,obj,
KlassHandle(THREAD,SystemDictionary::Thread_klass()),
vmSymbolHandles::run_method_name(),
vmSymbolHandles::void_method_signature(),THREAD);
}

这个vmSymbolHandles::run_method_name(),调用了run的方法,可是,光看方法名还是不能确定是不是run

所以我们在看看这个vmSymbolHandles,正好发现里面有这样一条语句,

1
template(run_method_name,"run")

成功的调用了run方法,所以,难怪在thread类中始终找不到相应的方法调用,原来是藏在了jvm的实现,cpp里面。

原来这就是它们之间的联系,从start到start0,再到native,再到jvm,再到cpp,其中的一个宏对象调用了run方法,那么我们再看看,这个run方法又写了啥

1
2
3
4
5
6
@Override
public void run()
{
if (target != null)
{ target.run(); }
}

可以看到,这里有一个target,通过它来判断,而这个target又在哪里?

1
private Runnable target;

哦,原来target在thread这个类中又是runnable所定义了,那runnable又是什么??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}

看到这里,俄罗斯套娃也终于结束了,所谓的runnable是一个接口,接口又声明必须要重写run这个方法。

也就是说,要么我们再开头创建的时候

要直接继承thread去声明,

要么直接在测试类中去接口一个runnable,这样才能够启动线程。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class test implements Runnable{
public static void main(String[] args) {
test r = new test();
Thread t = new Thread(r);
t.start();

}
@Override
public void run() {
System.out.println("xxxx");

}
}