`

多线程之线程状态

 
阅读更多

操作系统的多任务

有两种实现多任务的方法,这取决于操作系统在中断程序时的行为——直接中断而不需要事先和被中断的程序协商,还是只有在被中断程序同意交出控制权之后才能执行中断。前者称为抢占式多任务,后者称为协作(非抢占式)多任务。抢占式多任务更加有效,但实现起来难度较大。而在协作多任务机制下,一个行为不当的程序可能会独占所有资源,导致其他所有程序无法正常工作(时说不当的程序不交出控制权,其他的线程任务无法被执行)。

多线程程序在更低的层次中引入多任务从而扩展了多任务的思想:单个程序看起来可以同时处理多个任务。通常将每个任务称为一个线程,它是控制线程的简称。可以一次运行多个线程的程序被称为多线程程序。

 

多线程和多进程的区别

本质区别在于每个进程有它自己的变量的完备集,线程则共享相同的数据。这听起来似乎有些危险,事实上也确实如此。尽管如此,对程序来说,共享的变量使线程之间的通讯比进程间的通讯更加有效而简单。而且,对于某些操作系统而言,线程比进程更“轻量级”,创建和销毁单个线程比发起进程的开销要小得多。

 

你可以通过构建一个Thread的子类来定义一个线程,然后构造一个子类的对象并调用他的start方法,然而并不建议你这样做。应该尽量从机制上减少需要并行运行的任务数。如果你有很多任务,为每个任务都创建一个独立的线程的代价就太大了,推荐使用线程池。

 

警告:不要调用Thread类或者Runnable对象的run方法。直接调用run方法只会在当前线程中执行任务,并不会启动新的线程(相当于在当前线程中的一个普通方法调用),正确的做法是调用Thread.start()方法,它会创建一个新的线程来执行run方法。

 

API:java.lang.Thread

  • static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  • Thread(Runnable target)  构造一个新的线程来调用指定target的run方法
  • void start() 启动这个线程,将引发调用run方法。这个方法将立即返回,并且新线程将并发运行
  • void run() 调用关联的Runnable的run方法

中断线程

线程将在它的run方法返回时终止。

可以使用interrupt方法来请求终止一个线程。该方法被调用时,会发送一个中断请求给当前线程。这个线程的中断状态将被设为true。如果这个线程当前被一个sleep调用阻塞,那么将抛出一个InterruptedException异常。使用interrupt方法并不能杀死线程,他只是将线程标记为中断状态,中断状态是可能再次回到可运行状态的,请参考下文中的线程状态。

 

当interrupt方法在一个线程上被调用时,该线程的中断状态(interrupted status)将会被置位。这是一个布尔类型的标志,存在于每一个线程之中。每个线程都应该不时地检查这个标志,以判断线程是否应该被中断。为了查明中断状态是否被置位,需要首先调用静态的Thread.currentThread方法来取得当前线程,然后调用它的isInterrupted方法:

while (!Thread.currentThread().isInterrupted()) {
	//do more work
}

 

如果一个线程被阻塞(是指阻塞状态,有很多种场景可以让线程阻塞,比方说Thread的sleep或者wait方法都可以造成线程的暂时阻塞)了,他就无法检查中断状态,而且还会抛出一个InterruptedException异常。

 

Thread的sleep等方法可以造成当前线程的阻塞,因此不要在sleep方法之后调用isInterrupted方法。否则会抛异常。


警告:当sleep方法抛出一个InterruptedException异常的时候,它同时也会清除中断状态。

 

interrupted和isInterrupted两个方法的区别:

这两个方法都能用来检测线程是否处于中断状态。interrupted方法是Thread类的静态方法,调用interrupted方法后会清除当前线程的阻塞状态。而isInterrupted方法是一个实例方法,调用isInterrupted方法后不会改变中断状态的值。

 

线程状态

线程可以有以下4个状态:

  • New——新生状态
  • Runnable——可运行状态
  • Blocked——被阻塞状态
  • Dead——死亡状态

新生状态:

当使用new操作符创建一个线程时,例如:用new Thread(Runnable),线程还没有开始运行(还没有调用start方法)。此时线程处于新生状态。

 

可运行状态:

当调用start方法后,该线程就进入了可运行状态了。start方法调用后,一个可运行线程可能实际上正在运行,也可以没有被执行,这取决于操作系统为该线程提供的运行时间(也就是之前说到的操作系统的多任务机制,是抢占式的还是协作式的,可运行状态的线程可能没有抢占到执行权而没有运行)。进入可运行的状态的线程被运行以后,并不是始终保持运行时的状态,可能会在运行中被中断,目的是使其他线程获得运行机会,也可能被其他的线程抢过去执行了。

目前市面上的桌面和服务器的操作系统都是使用抢占式调度,只有一些小型设备如手机系统,可能会采用协作式调度。因此运行时的线程可能随时会被其他的线程抢占过去执行,所以我们的称这个状态为Runnable而不是Running。

 

被阻塞状态:

当发生以下任何一种情况时,线程就会进入被阻塞状态:

  • 线程通过调用sleep方法进入睡眠状态
  • 线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者
  • 线程试图得到一个锁,而该锁正被其他线程持有
  • 线程在等待某个触发条件
  • 有人调用了线程的suspend方法。该方法已过时,我们不应该在自己的代码中使用。

通过以下途径中的一种,线程可以从被阻塞状态回到可运行状态:

  • 线程被置于睡眠状态,且已经经过指定的毫秒数——睡眠时间到了,该醒了。
  • 线程正在等待I/O操作的完成,且该操作已经完成——你完成了,该我了。
  • 线程正在等待另一个线程所持有的锁,且另一个线程已经释放了该锁的所有权(也有可能等待超时,我不等了)。
  • 线程正在等待某一个触发条件,且另一个线程发出了信号表明条件已经发生变化(也有可能等待超时,我不等了)。
  • 线程已经被挂起,且有人调用了它的resume方法。和suspend方法一样,该方法已过时,我们不应该在自己的代码中使用。

一个被阻塞的线程只能通过和他先前阻塞他的相同过程重新进入可运行状态。要特别注意,不能通过调用resume方法来解除被阻塞线程的阻塞状态。也就是说,上文介绍的进入阻塞状态与回到可运行状态必须是相互对应的。

 

死亡状态:

有两个原因会导致线程死亡,如下:

  • 因为run方法正常退出而自然死亡
  • 因为一个未捕获的异常终止了run方法而使线程猝死

特殊情况下,可以通过调用线程的stop方法来杀死一个线程。该方法将抛出一个ThreadDeath出错对象来杀死线程。但stop方法已经过时,我们不应该在自己的代码中调用它。

 

如何判断当前线程是否存活(要么是可运行的,要么是被阻塞的)

可以使用isAlive方法。如果线程是可运行或者被阻塞的,该方法返回true,如果线程处于新生状态或者是死亡状态,则返回false。

 

注意:我们无法确定一个活着的线程是可运行的,还是被阻塞的,也无法确定一个可运行的线程是否正在运行。另外,你也无法区分死亡线程和非可运行线程。


参考资料:

JAVA 核心技术 卷2:高级特性

 

分享到:
评论

相关推荐

    大漠多线程模板_大漠_大漠多线程_

    "大漠多线程模板"是一个专门针对C#开发的多线程处理框架,它为开发者提供了便捷的方式来管理和优化多线程应用。这个框架由知名开发者"大漠"创建,旨在简化复杂的并发编程,提高代码的可读性和可维护性。 多线程允许...

    C#多线程读写sqlite

    在C#编程中,多线程技术常用于提高应用程序的执行效率,特别是在处理数据库操作时。SQLite是一款轻量级、嵌入式的关系型数据库,它广泛应用于桌面应用、移动设备和Web开发。当多线程环境对SQLite进行读写操作时,...

    C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例),可直接运行

    7. **线程状态管理**:`Thread`对象提供了多种属性,如`IsAlive`、`ThreadState`、`Priority`等,可以检查线程状态、调整优先级等。 8. **异常处理**:在多线程环境中,每个线程都有自己的异常处理机制,主线程不能...

    鱼刺多线程注册源码例子(鱼刺多线程稳定框架)

    "鱼刺多线程注册源码例子"是一个基于"鱼刺多线程稳定框架"的编程实践,旨在展示如何在软件开发中有效地利用多线程技术来提高程序的执行效率和稳定性。在这个例子中,"鱼刺框架"可能是一个专门为多线程编程设计的开源...

    多线程基础与基于多线程的简单聊天室

    10. **多线程的调试**:在开发过程中,利用Java的Thread类提供的堆栈信息和Thread Dump,可以帮助我们理解线程的执行状态,定位和解决问题。 通过学习和实践这个项目,你可以深入理解多线程的基本原理,以及如何将...

    C# 多线程实例多线程实例多线程实例

    5. 线程状态: 线程有多种状态,如新建、运行、等待、挂起等。了解这些状态有助于调试和优化多线程程序。 6. 异常处理: 在多线程环境中,每个线程都有自己的异常处理栈。主线程无法捕获到子线程中抛出的异常,...

    多线程的运用e语言多线程 e多线程

    6. 线程管理:监控线程状态,如等待、挂起、恢复、终止等。 三、线程同步与通信 在多线程环境中,数据共享可能会引发数据不一致问题,因此需要同步机制来保证数据的安全性。E语言中可能提供以下同步原语: 1. 锁...

    PB多线程实现

    本文将详细探讨PB(包括PB9、PB12.5以及PB.NET)实现多线程的方法。 一、PB9的多线程实现 在PB9中,虽然官方并未直接支持多线程,但开发者可以通过使用Windows API函数来实现。一种常见的方式是创建一个新的窗口类...

    .NET多线程实例

    在多线程环境中,事件处理通常在引发事件的线程上运行,因此,如果一个线程修改了UI控件的状态,而另一个线程尝试处理相关事件,可能会导致线程不安全。使用适当的同步机制可以解决这个问题。 "资源管理器"可能涉及...

    Java多线程知识点总结

    了解这些状态对于掌握Java多线程编程至关重要。 新建状态是指线程对象创建之后,此时线程尚未开始运行。就绪状态表示线程已经准备好运行,但CPU尚未分配时间片给它。运行状态是指线程获得CPU时间片后开始执行的过程...

    用VB6实现多线程

    在VB6(Visual Basic 6)环境中,多线程是一个重要的技术,它允许程序同时执行多个任务,提高程序的响应性和效率。VB6本身并不直接支持多线程,但可以通过调用Windows API来实现。本篇文章将深入探讨如何在VB6中实现...

    Java线程:线程状态的转换

    线程状态之间的转换是多线程编程的核心概念之一,理解这些转换有助于我们更好地设计和调试并发程序。 1. **新状态到可运行状态**:当线程对象的`start()`方法被调用时,线程从新状态进入可运行状态。 2. **可运行...

    delphi多线程调用dll

    如果DLL中的函数不是线程安全的,或者它们修改了全局状态或共享数据,那么在多线程调用时就需要使用同步原语来保护这些操作。例如,我们可以在调用DLL函数前后加锁,确保同一时间只有一个线程在执行: ```delphi ...

    多线程导入excel 数据

    - 通过监控工具(如JMX、VisualVM等)跟踪系统性能,观察CPU使用率、内存消耗、线程状态等,以便调整系统配置和代码优化。 综上所述,多线程导入Excel数据是一个涉及并发控制、线程同步、数据处理和性能优化的复杂...

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制是Java编程中至关重要的一部分,它允许程序同时执行多个任务,提升应用程序的效率和响应性。以下是对各个知识点的详细说明: 9.1 Java中的线程: Java程序中的线程是在操作系统级别的线程基础上进行...

    MFC多线程的创建,包括工作线程和用户界面线程

    ### MFC多线程的创建详解 #### 一、MFC多线程概述 MFC (Microsoft Foundation Classes) 是微软为简化Windows程序开发提供的一套类库,它封装了Win32 API,使得开发者能够更加方便地进行Windows应用程序的开发。在...

    delphi7 多线程测试(40个线程)

    1. **线程切换开销**:操作系统在管理多线程时需要频繁地进行上下文切换,即保存和恢复线程状态。这个过程会消耗CPU时间和内存资源,当线程数量增加时,这些开销会显著增加。 2. **CPU核心限制**:大多数现代计算机...

    多线程面试题

    3. **线程状态**:Java中的线程有五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和终止(Terminated)。理解这些状态以及它们之间的转换是分析多线程问题的关键。 4. **同步机制**:...

Global site tag (gtag.js) - Google Analytics