Java Thread系列(二)线程状态

一、线程的五种状态

  1. 新建状态(New):新创建了一个线程对象,尚未启动。
  2. 就绪状态(Runnable):也叫可运行状态。线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
  3. 运行状态(Running):就绪状态的线程获取了 CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    • 等待阻塞:运行的线程执行 wait() 方法,JVM 会把该线程放入等待池中。
    • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池中。
    • 其他阻塞:运行的线程执行 sleep() 或 join() 方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep() 状态超时、join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了 run() 方法,该线程结束生命周期。

二、线程调度

(1) 线程优先级

Java 线程有优先级,优先级高的线程会获得较多的运行机会。

Java 线程的优先级用整数表示,取值范围是 1~10,Thread 类有以下三个静态常量:

  1. static int MAX_PRIORITY // 线程可以具有的最高优先级,取值为 10。
  2. static int MIN_PRIORITY // 线程可以具有的最低优先级,取值为 1。
  3. 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()

  1. public class JoinTest extends Thread {
  2. @Override
  3. public void run() {
  4. try {
  5. Thread.sleep(2000);
  6. System.out.println("子线程:" + Thread.currentThread().getName());
  7. } catch (InterruptedException e) {
  8. ;
  9. }
  10. }
  11. public static void main(String[] args) throws InterruptedException {
  12. JoinTest t = new JoinTest();
  13. t.start();
  14. t.join(); // (1)
  15. System.out.println("main 线程:" + Thread.currentThread().getName());
  16. }
  17. }
  1. t.join() 会阻塞当前线程,即 main 线程,也就是说 main 线程会等待子线程执行完毕才会继续往下执行。

(3) Thread.yield()

Thread.yield() 方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

Thread.yield() 应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用 Thread.yield() 的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证 yield() 达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

(4) setPriority()

更改线程的优先级。

  1. MIN_PRIORITY = 1
  2. NORM_PRIORITY = 5
  3. 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 区别

共同点:

  1. 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。

  2. 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 。

不同点:

  1. Thread 类的方法:sleep(),yield() 等。Object的方法:wait() 和 notify() 等
  2. 每个对象都有一个锁来控制同步访问。Synchronized 关键字可以和对象的锁交互,来实现线程的同步。

    sleep 方法没有释放锁,而 wait 方法释放了锁,使得其他线程可以使用同步控制块或者方法。
  3. wait,notify 和 notifyAll 只能在同步控制方法或者同步控制块里面使用,而 sleep 可以在任何地方使用
  4. sleep 必须捕获异常,而 wait,notify 和 notifyAll 不需要捕获异常

所以 Thread.sleep() 与 Object.wait() 方法的最大区别是:sleep()睡眠时,保持对象锁,仍然占有该锁;而wait() 睡眠时,释放对象锁。但是 wait() 和 sleep() 都可以通过 interrupt() 方法打断线程的暂停状态,从而使线程立刻抛出 InterruptedException(但不建议使用该方法)。

四、常见线程名词解释

主线程:JVM 调用程序 main() 所产生的线程。

当前线程:这个是容易混淆的概念。一般指通过 Thread.currentThread() 来获取的进程。

后台线程(守护线程):指为其他线程提供服务的线程。JVM 的垃圾回收线程就是一个后台线程。用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束.

前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过 isDaemon() 和 setDaemon() 方法来判断和设置一个线程是否为后台线程。

Thread 线程类的一些常用方法:

  1. Thread.sleep(): 强迫一个线程睡眠N毫秒。
  2. isAlive(): 判断一个线程是否存活。
  3. join(): 等待线程终止。
  4. activeCount(): 程序中活跃的线程数。
  5. enumerate(): 枚举程序中的线程。
  6. currentThread(): 得到当前线程。
  7. isDaemon(): 一个线程是否为守护线程。
  8. setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
  9. setName(): 为线程设置一个名称。
  10. wait(): 强迫一个线程等待。
  11. notify(): 通知一个线程继续运行。
  12. setPriority(): 设置一个线程的优先级。

每天用心记录一点点。内容也许不重要,但习惯很重要!

Java Thread系列(二)线程状态的更多相关文章

  1. java并发系列(二)-----线程之间的协作(wait、notify、join、CountDownLatch、CyclicBarrier)

    在java中,线程之间的切换是由操作系统说了算的,操作系统会给每个线程分配一个时间片,在时间片到期之后,线程让出cpu资源,由其他线程一起抢夺,那么如果开发想自己去在一定程度上(因为没办法100%控制 ...

  2. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  3. JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态

    JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态  2017年10月25日  Jet Ma  JavaPlatform JVM故障分析系列系列文章 JVM故障分析系列之一: ...

  4. Java Thread系列(四)线程通信

    Java Thread系列(四)线程通信 一.传统通信 public static void main(String[] args) { //volatile实现两个线程间数据可见性 private ...

  5. Java Thread系列(三)线程安全

    Java Thread系列(三)线程安全 一.什么是线程安全 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的. 线程安全来 ...

  6. Java Thread系列(一)线程创建

    Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...

  7. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  8. Java Thread系列(十)Future 模式

    Java Thread系列(十)Future 模式 Future 模式适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量. 一.Future 模式核心思想 如下的请求 ...

  9. Java Thread系列(九)Master-Worker模式

    Java Thread系列(九)Master-Worker模式 Master-Worker模式是常用的并行设计模式. 一.Master-Worker 模式核心思想 Master-Worker 系统由两 ...

随机推荐

  1. localhost能连接websocket 127.0.0.1 不能连接问题?

    最近开发中遇到一个问题,就是有的浏览器电脑能连接websocket , 有的不能 , 有的能用localhost连接,有的能用127.0.0.1连接,这个问题很奇怪 提供一个很好测试websocket ...

  2. C语言 scanf()和gets()函数的区别

    C语言 scanf()和gets()函数的区别 1.相同点:scanf( )函数和gets( )函数都可用于输入字符串 2.不同点:两者在功能上有所区别,具体区别如下: 要实现如下需求“从控制台输入字 ...

  3. [TopCoder11557]MatrixPower

    vjudge description 你有一个\(n \times n\)的矩阵\(A\),下标从\(0\)开始,其中\(A_{i,j}=di + q^j\). 给你\(d,q,n,k,s,t\),求 ...

  4. test20181015 B 君的第三题

    题意 B 君的第三题(zhengzhou) 题目描述 让你在战争和耻辱中做一块选择,你选择耻辱,可你将来还得进行战争. 在平面上有n 个整点(横纵坐标都是整数) B 君想找到一个整点,使得这个点,到所 ...

  5. centos7 安装配置rsyslog + LogAnalyzer + mysql

    https://www.cnblogs.com/mchina/p/linux-centos-rsyslog-loganalyzer-mysql-log-server.html 安装LNMP 一键安装包 ...

  6. CentOS 7安装TigerVNC Server

    http://blog.csdn.net/wamath/article/details/76003128 1. CentOS 7安装TigerVNC Server 本文介绍如何在CentOS 7上安装 ...

  7. 黄聪:360、chrome开发插件扩展如何跨域调用其他网站的信息并且显示在扩展、tab中的api

    chrome插件提供了查找tab的api chrome.tabs.get(integer tabId, function callback) 但是出于安全的考虑,tab的属性中没有document 因 ...

  8. hadoop2.6.0的eclipse插件编译和设置

    编译hadoop2.6.0的eclipse插件 下载源码: git clone https://github.com/winghc/hadoop2x-eclipse-plugin.git 编译源码: ...

  9. Poly

    folly/Poly.h Poly is a class template that makes it relatively easy to define a type-erasing polymor ...

  10. React-Todos

    最近学完React的最基本概念,闲下来的时候就自己写了一个Todo-List的小应用.这里做个简略的说明,给想好好学React的新手看. React-Todo 学习前提 这里我用了webpackb做了 ...