Thread
Thread 就是在 Java 中,线程的代言人。系统中的一个线程,就对应到 Java 中的一个 Thread 对象。围绕线程的各种操作,都是通过 Thread 来展开的
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联
用我们上面的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, Strin name) | 使用 Runnable 对象创建线程对象,并命名 |
Thread t1 = new Thread(); Thread t2 = new Thread(new MyRunnable()); Thread t3 = new Thread("这是我的名字"); Thread t4 = new Thread(new MyRunnable(), "这是我的名字");```
Thread类的常见属性
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
- ID(getId):线程的身份表示(在JVM这里给线程设定的身份标识),一个线程可以有多个身份标识
- 名称(getName):各种调试工具用到
- 状态(getState):Java中的线程状态和操作系统中有一定差异
- 优先级(getPriority):设置/获取线程优先级作用不是很大,线程的调度主要还是系统内核来负责的,系统调度的速度实在太快
- 是否后台线程(isDaemon)
- 后台线程/守护线程:不太影响进程结束
- 前台线程:会影响进程结束,如果前台线程没执行完,进程是不会结束的
- 一个进程中所有的前台线程都执行完,退出了,此时即使存在后台线程仍然没执行完也会随着进程一起退出
- 影响进程退出的就是前台,不影响的就是后台
- 创建的线程默认是前台线程,通过setDaemon显式的设置成后台
- 是否存活(isAlive):Thread对象,对应的线程(系统内核中)是否存活。
- Thread对象的生命周期,并不是和系统中的线程完全一致的
- 是否被中断(isInterrupted):看下面
启动一个线程-start()
- start方法,在系统中真正创建出线程
- 创建出PCB
- 把PCB加入到对应链表
- 操作系统内核=内核+配套的程序
- 内核:一个系统最核心的功能
- 对下,管理好各种硬件设备
- 对上,给各种程序提供稳定的运行环境
- start方法本身执行成功是一瞬间的,只是告诉系统你要创建一个线程,调用完start后,代码就会立即继续执行start后续的逻辑
终止一个线程
- 一个线程的run方法执行完毕,就算终止了
- 此处的终止线程,就是想办法让run方法能够尽快执行完毕
- 方法:
1.程序员手动设定标志位,通过这个来让run尽快结束
private static class MyRunnable implements Runnable { public volatile boolean isQuit = false; @Override public void run() { while (!isQuit) { System.out.println(Thread.currentThread().getName() + ": 别管我,我忙着转账呢!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ": 啊!险些误了大事"); } } public static void main(String[] args) throws InterruptedException { MyRunnable target = new MyRunnable(); Thread thread = new Thread(target, "李四"); System.out.println(Thread.currentThread().getName() + ": 让李四开始转账。"); thread.start(); Thread.sleep(10 * 1000); System.out.println(Thread.currentThread().getName() + ": 老板来电话了,得赶紧通知李四对方是个骗子!"); target.isQuit = true; }
- 直接Thread类,给我们提供好了现成的标志位,不用手动设置标志位
private static class MyRunnable implements Runnable { @Override public void run() { // 两种方法均可以 while (!Thread.interrupted()) { //while (!Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + ": 别管我,我忙着转账呢!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println(Thread.currentThread().getName() + ": 有内鬼,终止交易!"); // 注意此处的 break break; } } System.out.println(Thread.currentThread().getName() + ": 啊!险些误了大事"); } } public static void main(String[] args) throws InterruptedException { MyRunnable target = new MyRunnable(); Thread thread = new Thread(target, "李四"); System.out.println(Thread.currentThread().getName() + ": 让李四开始转账。"); thread.start(); Thread.sleep(10 * 1000); System.out.println(Thread.currentThread().getName() + ": 老板来电话了,得赶紧通知李四对方是个骗子!"); thread.interrupt(); }
4.当sleep被唤醒以后,程序员可以有以下几种操作方式:
- 立即停止循环,立即结束线程(直接break)
- 继续做点别的事情,过一会儿再结束线程(catch中执行别的逻辑,执行完再break)
- 忽略终止的请求,继续循环(不写break)
5.thread 收到通知的方式有两种:
- 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
- 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程
2.否则,只是内部的一个中断标志被设置,thread 可以通过
- Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
- Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
等待一个线程-join()
- 多个线程是并发执行的。具体的执行过程都是由操作系统负责调度的。操作系统的调度线程的过程,是“随机”的。无法确定线程执行的先后顺序
- 等待线程,就是一种规划 线程结束 顺序的手段
- 注意:join()方法也会抛出Interrupted异常
- 谁调用join方法,谁就强占cpu资源,直至执行结束
- A B两个线程,希望B先结束A后结束,此时就可以让A线程中调用B.join()的方法。此时,B线程还没执行完,A线程就会进入"阻塞"状态。就相当于给B留下了执行的时间。B执行完毕之后,A再从阻塞状态中恢复回来,并且继续往后执行。如果A执行到B.join()的时候,B已经执行完了,A就不必阻塞了,直接往下执行就可以了
- 阻塞:让代码暂时不继续执行了(该线程暂时不去CPU上参与调度)
- sleep也能让线程阻塞,阻塞是有时间限制的
- join的阻塞,则是“死等”“不见不散”
- sleep、join、wait…产生阻塞之后,都是可能被interrupt方法唤醒的,这几个方法都会在被唤醒之后自动清除标志位(和sleep类似),这些方法都会抛出Interrupted异常
线程的状态
- 先谈谈进程里PCB里的状态字段:就绪和阻塞状态(这些是系统设定的状态,Java又把这些细分了)
- 以下则是线程的状态:
- NEW**(开始前)**: 安排了工作, 还未开始行动 (Thread对象创建好了,但还没有调用start()方法)
- RUNNABLE**(就绪状态): 可工作的. 又可以分成正在工作中和即将开始工作**
- 线程正在CPU上运行
- 线程正在排队,随时可以去CPU运行
- BLOCKED**(阻塞状态)**: 这几个都表示排队等着其他事情 :因为锁
- WAITING**(阻塞状态)**: 这几个都表示排队等着其他事情 :因为调用了wait
- TIMED_WAITING**(阻塞状态)**: 这几个都表示排队等着其他事情 :因为调用了sleep
- TERMINATED**(结束后)**: 工作完成了
- 线程状态转换图
文章知识点与官方知识档案匹配,可进一步学习相关知识