进程 & 线程的概念:
进程:在某种程度上表示相互隔离的、独立运行的程序。
线程:也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。
进程 & 线程的区别:
与分隔的进程相比,进程中的线程之间的隔离程度要小。
进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。
在 java 中,用 Thread 类表示线程。
在 Thtead 的内部有个枚举类,它定义了线程的状态:
public enum State { NEW, // 新建状态 RUNNABLE, // 就绪状态(即可执行状态) BLOCKED, // 阻塞状态 WAITING, // 等待状态 TIMED_WAITING, // 等待状态(有时间限制) TERMINATED; // 死亡状态}NEW ,指线程刚创建, 尚未启动。
RUNNABLE ,表示线程可以正常参与竞争 CPU 资源,成功率与其优先级高低有关。
BLOCKED,表示线程进入阻塞并监视锁的状态。
它存在于多个线程有同步操作的场景。比如正在等待另一个线程的 synchronized 块的执行释放。
WAITING ,该状态下的线程表示正在等待另外一个线程的动作。
例如:
调用了 wait 方法且没有超时,进入等待状态,等待另一个线程调用 notify/notiyfAll 方法。调用了 join 方法且没有超时 ,进入等待状态,等待另一个线程终止。调用了 LockSupport.park ,进入等待状态,等待另一个线程调用 LockSupport.park。TIMED_WAITING ,存在于:
调用了 wait / wait(long) 方法且没有超时 调用了 join 方法且没有超时调用了 LockSupport.parkNanos/LockSupport.parkUntil 方法线程的优先级具有以下特点:
优先级代表获取 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());}下面来看它们的具体实现:
interrupt,设置线程的中断状态// 用于同步,表示锁对象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();若想要令线程进入阻塞状态,可以调用 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();}新闻热点
疑难解答