Java Thread系列(二)线程状态
Java Thread系列(二)线程状态
一、线程的五种状态
- 新建状态(New):新创建了一个线程对象,尚未启动。
- 就绪状态(Runnable):也叫可运行状态。线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
- 运行状态(Running):就绪状态的线程获取了 CPU,执行程序代码。
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
- 等待阻塞:运行的线程执行 wait() 方法,JVM 会把该线程放入等待池中。
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池中。
- 其他阻塞:运行的线程执行 sleep() 或 join() 方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep() 状态超时、join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead):线程执行完了或者因异常退出了 run() 方法,该线程结束生命周期。
二、线程调度
(1) 线程优先级
Java 线程有优先级,优先级高的线程会获得较多的运行机会。
Java 线程的优先级用整数表示,取值范围是 1~10,Thread 类有以下三个静态常量:
static int MAX_PRIORITY // 线程可以具有的最高优先级,取值为 10。
static int MIN_PRIORITY // 线程可以具有的最低优先级,取值为 1。
static int NORM_PRIORITY // 分配给线程的默认优先级,取值为 5。
Thread 类的 setPriority() 和 getPriority() 方法分别用来设置和获取线程的优先级。
每个线程都有默认的优先级。主线程的默认优先级为 Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如 A 线程中创建了 B 线程,那么 B 将和 A 具有相同的优先级。
JVM 提供了 10 个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用 Thread 类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。
(2) 线程睡眠
Thread.sleep(long millis) 方法,使线程转到阻塞状态。millis 参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep() 平台移植性好。
(3) 线程等待
Object 类中的 wait() 方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是 Object 类中的方法,行为等价于调用 wait(0) 一样。
(4) 线程让步
Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
(5) 线程 join
join() 方法,等待其他线程终止。在当前线程中调用另一个线程的 join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
(6) 线程唤醒
Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个 notifyAll(),唤醒在此对象监视器上等待的所有线程。
注意:Thread 中 suspend() 和 resume() 两个方法在 JDK1.5 中已经废除,不再介绍。因为有死锁倾向。
三、常用函数说明
(1) Thread.sleep(long millis)
sleep() 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
sleep() 不能改变对象的机锁,所以当在一个 Synchronized 块中调用 Sleep() 方法时,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
在 sleep() 休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
(2) join()
public class JoinTest extends Thread {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("子线程:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
;
}
}
public static void main(String[] args) throws InterruptedException {
JoinTest t = new JoinTest();
t.start();
t.join(); // (1)
System.out.println("main 线程:" + Thread.currentThread().getName());
}
}
- t.join() 会阻塞当前线程,即 main 线程,也就是说 main 线程会等待子线程执行完毕才会继续往下执行。
(3) Thread.yield()
Thread.yield() 方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
Thread.yield() 应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用 Thread.yield() 的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证 yield() 达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
(4) setPriority()
更改线程的优先级。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
(5) interrupt()
不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出异常,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!
(6) wait()
wait() 方法是 Object 类里的方法;当一个线程执行到 wait() 方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;
wait() 使用 notify 或者 notifyAlll 或者指定睡眠时间来唤醒当前等待池中的线程。
wiat() 必须放在 synchronized block 中,否则会在 program runtime 时扔出 “java.lang.IllegalMonitorStateException” 异常。
wait 和 sleep 区别
共同点:
他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
wait() 和 sleep() 都可以通过 interrupt() 方法 打断线程的暂停状态 ,从而使线程立刻抛出 InterruptedException。
如果线程 A 希望立即结束线程 B,则可以对线程 B 对应的 Thread 实例调用 interrupt 方法。如果此刻线程 B 正在 wait/sleep /join,则线程 B 会立刻抛出 InterruptedException,在 catch() {} 中直接 return 即可安全地结束线程。
需要注意的是,InterruptedException 是线程自己从内部抛出的,并不是 interrupt() 方法抛出的。对某一线程调用 interrupt() 时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出 InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join() 后,就会立刻抛出 InterruptedException 。
不同点:
- Thread 类的方法:sleep(),yield() 等。Object的方法:wait() 和 notify() 等
- 每个对象都有一个锁来控制同步访问。Synchronized 关键字可以和对象的锁交互,来实现线程的同步。
sleep 方法没有释放锁,而 wait 方法释放了锁,使得其他线程可以使用同步控制块或者方法。 - wait,notify 和 notifyAll 只能在同步控制方法或者同步控制块里面使用,而 sleep 可以在任何地方使用
- sleep 必须捕获异常,而 wait,notify 和 notifyAll 不需要捕获异常
所以 Thread.sleep() 与 Object.wait() 方法的最大区别是:sleep()睡眠时,保持对象锁,仍然占有该锁;而wait() 睡眠时,释放对象锁。但是 wait() 和 sleep() 都可以通过 interrupt() 方法打断线程的暂停状态,从而使线程立刻抛出 InterruptedException(但不建议使用该方法)。
四、常见线程名词解释
主线程:JVM 调用程序 main() 所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过 Thread.currentThread() 来获取的进程。
后台线程(守护线程):指为其他线程提供服务的线程。JVM 的垃圾回收线程就是一个后台线程。用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束.
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过 isDaemon() 和 setDaemon() 方法来判断和设置一个线程是否为后台线程。
Thread 线程类的一些常用方法:
Thread.sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。
每天用心记录一点点。内容也许不重要,但习惯很重要!
Java Thread系列(二)线程状态的更多相关文章
- java并发系列(二)-----线程之间的协作(wait、notify、join、CountDownLatch、CyclicBarrier)
在java中,线程之间的切换是由操作系统说了算的,操作系统会给每个线程分配一个时间片,在时间片到期之后,线程让出cpu资源,由其他线程一起抢夺,那么如果开发想自己去在一定程度上(因为没办法100%控制 ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态
JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态 2017年10月25日 Jet Ma JavaPlatform JVM故障分析系列系列文章 JVM故障分析系列之一: ...
- Java Thread系列(四)线程通信
Java Thread系列(四)线程通信 一.传统通信 public static void main(String[] args) { //volatile实现两个线程间数据可见性 private ...
- Java Thread系列(三)线程安全
Java Thread系列(三)线程安全 一.什么是线程安全 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的. 线程安全来 ...
- Java Thread系列(一)线程创建
Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java Thread系列(十)Future 模式
Java Thread系列(十)Future 模式 Future 模式适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量. 一.Future 模式核心思想 如下的请求 ...
- Java Thread系列(九)Master-Worker模式
Java Thread系列(九)Master-Worker模式 Master-Worker模式是常用的并行设计模式. 一.Master-Worker 模式核心思想 Master-Worker 系统由两 ...
随机推荐
- Loj 2005 相关分析
Loj 2005 相关分析 大力把式子拆开. \[ \begin{aligned} a &= \frac {\sum_{i=L}^{R} (x_i-\bar{x})(y_i-\bar{y})} ...
- zoj 1828 Fibonacci Numbers
A Fibonacci sequence is calculated by adding the previous two members of the sequence, with the firs ...
- Redis 集群方案介绍
由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法 ...
- popup控件代码示例
1.jsp页面input框中的代码 <td class="value"> <input name="demos[0].id" type=&qu ...
- Form中的keypress事件不能用
Form中的keypress事件不能用 编写人:CC阿爸 2015-4-8 近期在修改系统时,想给一画面增加一个组合键功能,但在form_keypress事件中加入代码,但无论如何也不能触发该动作. ...
- mysql-13处理重复数据
1.防止表中出现重复数据 在mysql数据表中设置指定的字段为主键或唯一索引来保证数据的唯一行. -- 方法1:指定主键 create `table person_tbl`( `first_name` ...
- CentOS 上使用vscode 调试百度大数据分析框架Apache Doris BE
A: 前期准备工作 1. 安装vscode,详细请参见vscode官网https://code.visualstudio.com/docs/setup/linux,摘要如下: sudo rpm --i ...
- id取模分表
场景 1 假设按用户id分2个库 每个库分10张表. 分表策略 1.用户id%2 确定库 用户id%3确定表. 2.(用户id%(2*10))/ 10 取整确定库,(用户id%(2*10)%10确 ...
- 好久不见(致win7)
7月8号,电脑上装了pgp,然后说让重启,重启之后蓝屏,自此,就一直蓝屏了 电脑装了双系统,工作时用centos,我不愿重装系统,怕centos受影响 网上说安装模式下可以卸载软件,可我在安全模式下, ...
- Spring batch学习 (1)
Spring Batch 批处理框架 埃森哲和Spring Source研发 主要解决批处理数据的问题,包含并行处理,事务处理机制等.具有健壮性 可扩展,和自带的监控功能,并且支持断点和重发.让程序员 ...