进程 & 线程的概念:
进程:在某种程度上表示相互隔离的、独立运行的程序。
线程:也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。
进程 & 线程的区别:
与分隔的进程相比,进程中的线程之间的隔离程度要小。
进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。
在 Java 中,用 Thread 类表示线程。
在 Thtead 的内部有个枚举类,它定义了线程的状态:
public enum State {
NEW, // 新建状态
RUNNABLE, // 就绪状态(即可执行状态)
BLOCKED, // 阻塞状态
WAITING, // 等待状态
TIMED_WAITING, // 等待状态(有时间限制)
TERMINATED; // 死亡状态
}
NEW ,指线程刚创建, 尚未启动。
RUNNABLE ,表示线程可以正常参与竞争 CPU 资源,成功率与其优先级高低有关。
BLOCKED,表示线程进入阻塞并监视锁的状态。
它存在于多个线程有同步操作的场景。比如正在等待另一个线程的 synchronized 块的执行释放。
WAITING ,该状态下的线程表示正在等待另外一个线程的动作。
例如:
TIMED_WAITING ,存在于:
线程的优先级具有以下特点:
优先级代表获取 CPU 资源的概率,优先级高的线程获取的概率较大。
优先级无法保证线程的执行次序,因为这个一个概率问题。
线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5。
在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。
在 Thhtead 类中,定义了三个优先级常量:
// 最小
public final static int MIN_PRIORITY = 1;
// 默认
public final static int NORM_PRIORITY = 5;
// 最大
public final static int MAX_PRIORITY = 10;
在 Java 中,用 Thread 表示线程类,它实现了 Runnbale 接口。
首先来看它的签名:
// 签名
public class Thread implements Runnable
// 接口
public interface Runnable {
public abstract void run();
}
再来看它的部分构造函数:
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
init(g, target, name, stackSize, null);
}
// 关键
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc){...}
从代码可知,构建线程的真正过程在 init 方法中实现。构建一个线程所需要的必要参数有:
ThreadGroup :线程组,表示一个线程的集合。此外,线程组也可以包含其他线程组。线程组构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组。
target:目标,即运行内容,可通过 run 方法执行。
name:线程名称,默认是 Thread-id,每个线程都不有不同的线程名。
stackSize:开辟新增线程所需的栈空间,为 0 表示忽略该参数。
AccessControlContext :上下文。
构建一个线程后,可以通过 start 方法来启动它,同时也表示线程进入就绪状态。
同样的,也可以同调用 Thread 的 yeid 方法来已经运行线程重新进入就绪状态。
yield
public static native void yield();
start
// 线程状态,默认为 0,表示线程还未启动,即 NEW 状态
private volatile int threadStatus = 0;
// 表示线程的线程组
private ThreadGroup group;
public synchronized void start() {
if (threadStatus != 0){
//抛出异常...
}
// 加入线程组
group.add(this);
boolean started = false;
try {
// 关键
start0();
started = true;
} finally {
try {
// 启动失败,通知线程组
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// do nothing...
}
}
}
// 使用了本地调用,通过 C 代码初始化线程需要的系统资源。
private native void start0();
线程在启动之后,会自动调用 run 方法,执行规定好的内容。
private Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
由代码可知,线程在启动后会自动执行 Runnable.run 定义的内容。
线程中断,并不是指中断线程的运行,而是指设置线程的中断标记位。
在 Thread 类中,有三个方法跟中断标记位有关:
public static void main(String[] args) {
// interrupt:设置线程的中断状态
Thread.currentThread().interrupt();
// interrupted:返回线程的上次的中断状态,并清除中断状态
System.out.println(Thread.currentThread().interrupted());
// isInterrupt:返回线程的上次的中断状态
System.out.println(Thread.currentThread().isInterrupted());
}
下面来看它们的具体实现:
// 用于同步,表示锁对象
private final Object blockerLock = new Object();
private volatile Interruptible blocker;
public void interrupt() {
if (this != Thread.currentThread()){
// 检查系统访问权限
checkAccess();
}
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
// 关键 -> 调用本地方法,设置线程中断标志位
interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
// 设置线程中断标志位
private native void interrupt0();
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public static native Thread currentThread();
// 入参表示是否清除线程中断标记位
private native boolean isInterrupted(boolean ClearInterrupted);
public boolean isInterrupted() {
return isInterrupted(false);
}
若想要令线程进入阻塞状态,可以调用 Thread 类的 sleep 方法实现。
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
// 抛出异常...
}
if (nanos < 0 || nanos > 999999) {
// 抛出异常...
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
public static native void sleep(long millis) throws InterruptedException;
在 Thread 类中,想要让线程进入等待,可以调用 join 方法实现。
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("t1 完成工作");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
// 关键把 t1 线程加入到当前线程,等待 t1 执行结束,main 才能继续执行
t1.join();
System.out.println("main 完成工作");
下面再来看看它的具体实现:
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
// 抛出异常...
}
if (millis == 0) {
// 关键 -> 线程(指代例子中 t1)存活,则(main)进入等待
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
// 判断线程是否存活
public final native boolean isAlive();
这里同样有个疑问,既然 mian 进入等待,那么在 t1 结束后如何被唤醒?
正常情况下进入 wait 的 线程,需要其他线程调用 notify/notifyAll 来唤醒。答案在 t1 线程结束后在 JVM 中会调用 notify 方法:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) ;
// 确保 join 函数
ensure_join(this);
static void ensure_join(JavaThread* thread) {
Handle threadObj(thread, thread->threadObj());
ObjectLocker lock(threadObj, thread);
thread->clear_pending_exception();
java_lang_Thread::set_thread_status(threadObj(),
java_lang_Thread::TERMINATED);
java_lang_Thread::set_thread(threadObj(), NULL);
// 关键 -> 调用线程的 notifyAll 方法。
lock.notify_all(thread);
thread->clear_pending_exception();
}