目录
1、简介
2、进程和线程对比
3、线程的状态:线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
4、线程启动的几种方式
5、 线程start方法到底做了什么,怎么回调到run方法
6、sleep和wait的区别
1、简介
线程的概念:CPU调度的最小单元
2、进程和线程对比
(1)、进程是资源分配最小单位,线程是程序执行的最小单位
(2)、进程有自己独立的地址空间,线程没有独立的地址空间
(3)、CPU切换一个线程比切换进程花费小,线程比进程开销小
(4)、进程对资源保护要求高,线程资源保护不高。
3、线程的状态:线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
新建状态(New):new一个Thread() 对象
就绪状态(Runnable): 新建一个线程不会自动运行,而是需要start开启后,进入就绪状态,然后在获取cpu选中才能进如运行状态,执行run方法
运行状态(Running):当线程处于准备就绪状态,并获得cpu时间资源后,才进入了运行状态
阻塞状态(Blocked):当线程因为各种原因,比如sleep操作,io被阻止、想获取一个被锁资源等原因导致线程被阻塞
死亡状态(Dead):run方法自然结束后或者因为某个未捕获异常而导致线程终止
图如下:(图来源于网上)
4、线程启动的几种方式
根据jdk官方api,有两种方式https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Thread.html
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread
. This subclass should override the run
method of class Thread
.The other way to create a thread is to declare a class that implements the Runnable
interface. That class then implements the run
method. An instance of the class can then be allocated, passed as an argument when creating Thread
, and started.
一种是继承Thread,复写run方法,然后start
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
The following code would then create a thread and start it running:
PrimeThread p = new PrimeThread(143);
p.start();
另外一种,实现Runnable接口,再Thread有参数构造,start
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
The following code would then create a thread and start it running:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
我看网上很多文章说还有FutureTask 啥的方式,这个其实就是Runnable接口的变种方式。可以看到源码:
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
5、 线程start方法到底做了什么,怎么回调到run方法
我们可以通过源码可以看到 start回调用native层nativeCreate方法
start线程的方法,代码如下
public synchronized void start() {
if (started)
throw new IllegalThreadStateException();
started = false;
try {
nativeCreate(this, stackSize, daemon);
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 */
}
}
}
那么怎么在开启后回调到run方法呢?先看下nativeCreate方法定义
// Android-changed: Use Android specific nativeCreate() method to create/start thread.
// The upstream native method start0() only takes a reference to this object and so must obtain
// the stack size and daemon status directly from the field whereas Android supplies the values
// explicitly on the method call.
// private native void start0();
private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
看注释我们可以看到有个start0() 方法,旧版本是调用start0()这个方法,由于android-30 lang 下面Thread.java 对应的c代码不好找,我退而求其次,去找了jdk7里面Thread.cpp 源码
http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/native/java/lang/Thread.c
还有个网址:http://androidxref.com/
这个类里面有定义start0() 这个方法
#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 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},
};
#undef THD
#undef OBJ
#undef STE
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
从这个cpp文件可以找到 start0()的定义
{"start0", "()V", (void *)&JVM_StartThread},
可以看这篇文章的直接通过Hotspot JVM 源码找到
最后会c调用java的 Thread 的 run方法
template(run_method_name, "run")
我们直接看thread的 run方法做了什么
@Override
public void run() {
if (target != null) {
target.run();
}
}
这个target是 Runnable实现类,所以会回调run方法执行操作
6、sleep和wait的区别
sleep这个方法是在Thread这个类里面,wait则在Object类中
sleep方法的过程中,线程不会释放对象锁,wait方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
wait来说使用之前要获取到锁的存在,所以必须放在同步代码,或者同步中进行执行 但是 sleep来说可以放在任何的地方执行 。
sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。wait则需要被唤醒
sleep需要捕获异常 ,wait notify 等不需要这些。