聚集网(jujiwang.com) - 收录免费分类目录信息软文发布网址提交
免费加入

提升 Java 多线程:超越 suspendThread 实现高效的线程管理 (提升Java的典型事件)

文章编号:11439时间:2024-09-30人气:


提升

在构建高性能、可扩展的 Java 应用程序时,充分利用多线程至关重要。如果不慎使用,线程也可能成为性能瓶颈,甚至导致不可预测的行为。

suspendThread 的局限性

suspendThread 方法是一种过时的线程管理技术,在 Java 9 及更高版本中已被弃用。它通过暂停线程的执行来冻结线程,但在某些情况下可能导致死锁和不可预测的行为。因此,在现代 Java 应用程序中使用 suspendThread 并不是一个好习惯。

Java

替代 suspendThread 的高效线程管理技术

有几种现代技术可以用来高效地管理线程,避免了 suspendThread 的缺点。以下是一些常见的方法:

1. wait() 和 notify()

实现高效的线程管理

wait() 和 notify() 方法允许线程等待特定条件满足后再继续执行。这对于实现生产者-消费者模式和同步线程访问共享资源非常有用。

2. Lock 和 Condition

Lock 和 Condition 类提供了一种更灵活的方式来管理线程同步。它们允许线程在获取锁后执行某些操作,并在特定条件满足时释放锁。

3. CountDownLatch

CountDownLatch 类用于等待一组线程完成特定任务。它提供了一个计数器,在所有线程完成任务后减为零,从而允许主线程继续执行。

4. Semaphore

Semaphore 类用于限制对共享资源的并发访问。它允许指定资源的最大访问数,并强制线程在访问资源之前获取许可证。

案例研究:使用 Lock 和 Condition

假设我们要构建一个简单的生产者-消费者应用程序,其中生产者线程将数据放入队列,而消费者线程将数据从队列中取出。我们可以使用 Lock 和 Condition 如下实现它:

java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Condition;

什么是线程 单独线程 多线程

一、线程的概念一般来说,我们把正在计算机中执行的程序叫做进程(Process) ,而不将其称为程序(Program)。 所谓线程(Thread),是进程中某个单一顺序的控制流。 新兴的操作系统,如Mac,Windows NT,Windows 95等,大多采用多线程的概念,把线程视为基本执行单位。 线程也是Java中的相当重要的组成部分之一。 甚至最简单的Applet也是由多个线程来完成的。 在Java中,任何一个Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)绘图与事件处理线程调用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()——是由执行该Applet的应用调用的。 单线程的概念没有什么新的地方,真正有趣的是在一个程序中同时使用多个线程来完成不同的任务。 某些地方用轻量进程(Lightweig ht Process)来代替线程,线程与真正进程的相似性在于它们都是单一顺序控制流。 然而线程被认为轻量是由于它运行于整个程序的上下文内,能使用整个程序共有的资源和程序环境。 作为单一顺序控制流,在运行的程序内线程必须拥有一些资源作为必要的开销。 例如,必须有执行堆栈和程序计数器。 在线程内执行的代码只在它的上下文中起作用,因此某些地方用执行上下文来代替线程。 二、线程属性为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。 必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。 (1)线程体所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法。 当线程产生并初始化后,实时系统调用它的run()方法。 run()方法内的代码实现所产生线程的行为,它是线程的主要部分。 (2)线程状态附图表示了线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法。 这图并不是完整的有限状态图,但基本概括了线程中比较感兴趣和普遍的方面。 以下讨论有关线程生命周期以此为据。 ●新线程态(New Thread)产生一个Thread对象就生成一个新线程。 当线程处于新线程状态时,仅仅是一个空线程对象,它还没有分配到系统资源。 因此只能启动或终止它。 任何其他操作都会引发异常。 ●可运行态(Runnable)start()方法产生运行线程所必须的资源,调度线程执行,并且调用线程的run()方法。 在这时线程处于可运行态。 该状态不称为运行态是因为这时的线程并不总是一直占用处理机。 特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理 机。 Java通过调度来实现多线程对处理机的共享。 ●非运行态(Not Runnable)当以下事件发生时,线程进入非运行态。 ①suspend()方法被调用;②sleep()方法被调用;③线程使用wait()来等待条件变量;④线程处于I/O等待。 ●死亡态(Dead)当run()方法返回,或别的线程调用stop()方法,线程进入死亡态 。 通常Applet使用它的stop()方法来终止它产生的所有线程。 (3)线程优先级虽然我们说线程是并发运行的。 然而事实常常并非如此。 正如前面谈到的,当系统中只有一个CPU时,以某种顺序在单CPU情况下执行多线程被称为调度(scheduling)。 Java采用的是一种简单、固定的调度法,即固定优先级调度。 这种算法是根据处于可运行态线程的相对优先级来实行调度。 当线程产生时,它继承原线程的优先级。 在需要时可对优先级进行修改。 在任何时刻,如果有多条线程等待运行,系统选择优先级最高的可运行线程运行。 只有当它停止、自动放弃、或由于某种原因成为非运行态低优先级的线程才能运行。 如果两个线程具有相同的优先级,它们将被交替地运行。 Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行。 (4)幽灵线程任何一个Java线程都能成为幽灵线程。 它是作为运行于同一个进程内的对象和线程的服务提供者。 例如,HotJava浏览器有一个称为 后台图片阅读器的幽灵线程,它为需要图片的对象和线程从文件系统或网络读入图片。 幽灵线程是应用中典型的独立线程。 它为同一应用中的其他对象和线程提供服务。 幽灵线程的run()方法一般都是无限循环,等待服务请求。 (5)线程组每个Java线程都是某个线程组的成员。 线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行整体操作。 譬如,你能用一个方法调用来启动或挂起组内的所有线程。 Java线程组由ThreadGroup类实现。 当线程产生时,可以指定线程组或由实时系统将其放入某个缺省的线程组内。 线程只能属于一个线程组,并且当线程产生后不能改变它所属的线程组。 三、多线程程序对于多线程的好处这就不多说了。 但是,它同样也带来了某些新的麻烦。 只要在设计程序时特别小心留意,克服这些麻烦并不算太困难。 (1)同步线程许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态。 这就需要同步机制。 在Java中每个对象都有一把锁与之对应。 但Java不提供单独的lock和unlock操作。 它由高层的结构隐式实现, 来保证操作的对应。 (然而,我们注意到Java虚拟机提供单独的monito renter和monitorexit指令来实现lock和unlock操作。 )synchronized语句计算一个对象引用,试图对该对象完成锁操作, 并且在完成锁操作前停止处理。 当锁操作完成synchronized语句体得到执行。 当语句体执行完毕(无论正常或异常),解锁操作自动完成。 作为面向对象的语言,synchronized经常与方法连用。 一种比较好的办法是,如果某个变量由一个线程赋值并由别的线程引用或赋值,那么所有对该变量的访问都必须在某个synchromized语句或synchronized方法内。 现在假设一种情况:线程1与线程2都要访问某个数据区,并且要求线程1的访问先于线程2, 则这时仅用synchronized是不能解决问题的。 这在Unix或Windows NT中可用Simaphore来实现。 而Java并不提供。 在Java中提供的是wait()和notify()机制。 使用如下:synchronized method-1(…){ call by thread 1.‖access data area;available=true;notify()}synchronized method-2(…){‖call by thread (!available)try{wait();‖wait for notify().}catch (Interrupted Exception e){}‖access data area}其中available是类成员变量,置初值为false。 如果在method-2中检查available为假,则调用wait()。 wait()的作用是使线程2进入非运行态,并且解锁。 在这种情况下,method-1可以被线程1调用。 当执行notify()后。 线程2由非运行态转变为可运行态。 当method-1调用返回后。 线程2可重新对该对象加锁,加锁成功后执行wait()返回后的指令。 这种机制也能适用于其他更复杂的情况。 (2)死锁如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。 系统均衡是指每个线程在执行过程中都能充分访问有限的资源。 系统中没有饿死和死锁的线程。 Java并不提供对死锁的检测机制。 对大多数的Java程序员来说防止死锁是一种较好的选择。 最简单的防止死锁的方法是对竞争的资源引入序号,如果一个线程需要几个资源,那么它必须先得到小序号的资源,再申请大序号的资源。 四、线程和进程的比较进程是资源分配的基本单位。 所有与该进程有关的资源,都被记录在进程控制块PCB中。 以表示该进程拥有这些资源或正在使用它们。 另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。 与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。 当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。 线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。 寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。 发生进程切换与发生线程切换时相比较,进程切换时涉及到有关资源指针的保存以及地址空间的变化等问题;线程切换时,由于同不进程内的线程共享资源和地址 空间,将不涉及资源信息的保存和地址变化问题,从而减少了操作系统的开销时间。 而且,进程的调度与切换都是由操作系统内核完成,而线程则既可由操作系统内 核完成,也可由用户程序进行。 五、线程的适用范围典型的应用 1.服务器中的文件管理或通信控制 2.前后台处理 3.异步处理六、线程的执行特性一个线程必须处于如下四种可能的状态之一:初始态:一个线程调用了new方法之后,并在调用start方法之前的所处状态。 在初始态中,可以调用start和stop方法。 Runnable:一旦线程调用了start 方法,线程就转到Runnable 状态,注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还有优先级和调度问题。 阻塞/ NonRunnable:线程处于阻塞/NonRunnable状态,这是由两种可能性造成的:要么是因挂起而暂停的,要么是由于某些原因而阻塞的,例如包括等待IO请求的完成。 退出:线程转到退出状态,这有两种可能性,要么是run方法执行结束,要么是调用了stop方法。 最后一个概念就是线程的优先级,线程可以设定优先级,高优先级的线程可以安排在低优先级线程之前完成。 一个应用程序可以通过使用线程中的方法setPriority(int),来设置线程的优先级大小。 线程有5种基本操作: 派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。 阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。 激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。 调度(schedule):选择一个就绪线程进入执行状态。 结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。 七、线程的分类线程有两个基本类型: 用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。 系统级线程(核心级线程):由操作系统内核进行管理。 操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行、撤消线程。 附:线程举例1. SUN Solaris 2.3Solaris支持内核线程、轻权进程和用户线程。 一个进程可有大量用户线程;大量用户线程复用少量的轻权进程,轻权进程与内核线程一一对应。 用户级线程在调用核心服务时(如文件读写),需要“捆绑(bound)”在一个LWP上。 永久捆绑(一个LWP固定被一个用户级线程占用,该LWP移到LWP池之外)和临时捆绑(从LWP池中临时分配一个未被占用的LWP)。 在调用系统服务时,如果所有LWP已被其他用户级线程所占用(捆绑),则该线程阻塞直到有可用的LWP。 如果LWP执行系统线程时阻塞(如read()调用),则当前捆绑在LWP上的用户级线程也阻塞。 ¨有关的C库函数/* 创建用户级线程 */int thr_create(void *stack_base, size_t stack_size,void *(*start_routine)(void *), void *arg, long flags,thread_t *new_thread_id);其中flags包括:THR_BOUND(永久捆绑), THR_NEW_LWP(创建新LWP放入LWP池),若两者同时指定则创建两个新LWP,一个永久捆绑而另一个放入LWP池。 ²有关的系统调用 /* 在当前进程中创建LWP */int _lwp_create(ucontext_t *contextp, unsigned long flags,lwpid_t *new_lwp_id);/* 构造LWP上下文*/void _lwp_makecontext(ucontext_t *ucp, void (*start_routine)( void *), void *arg,void *private, caddr_t stack_base, size_t stack_size);/* 注意:没有进行“捆绑”操作的系统调用 */2. Windows NTNT线程的上下文包括:寄存器、核心栈、线程环境块和用户栈。 NT线程状态 (1) 就绪状态:进程已获得除处理机外的所需资源,等待执行。 (2) 备用状态:特定处理器的执行对象,系统中每个处理器上只能有一个处于备用状态的线程。 (3) 运行状态:完成描述表切换,线程进入运行状态,直到内核抢先、时间片用完、线程终止或进行等待状态。 (4) 等待状态:线程等待对象句柄,以同步它的执行。 等待结束时,根据优先级进入运行、就绪状态。 (5) 转换状态:线程在准备执行而其内核堆栈处于外存时,线程进入转换状态;当其内核堆栈调回内存,线程进入就绪状态。 (6) 终止状态:线程执行完就进入终止状态;如执行体有一指向线程对象的指针,可将线程对象重新初始化,并再次使用。 NT线程的有关APICreateThread()函数在调用进程的地址空间上创建一个线程,以执行指定的函数;返回值为所创建线程的句柄。 ExitThread()函数用于结束本线程。 SuspendThread()函数用于挂起指定的线程。 ResumeThread()函数递减指定线程的挂起计数,挂起计数为0时,线程恢复执行。

线程的线程的同步

线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?对于同步,在具体的Java代码中需要完成以下两个操作:把竞争访问的资源标识为private;同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。 当然这不是唯一控制并发安全的途径。 synchronized关键字使用说明synchronized只能标记非抽象的方法,不能标识成员变量。 为了演示同步方法的使用,构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。 显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。 工作原理线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。 线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。 线程可以创建和撤消线程,从而实现程序的并发执行。 一般,线程具有就绪、阻塞和运行三种基本状态。 在多中央处理器的系统里,不同线程可以同时在不同的中央处理器上运行,甚至当它们属于同一个进程时也是如此。 大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度(affinity)。 有时候,线程也称作轻量级进程。 就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。 但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。 它们共享内存、文件句柄和其它每个进程应有的状态。 进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。 一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。 尽管这让线程之间共享信息变得更容易,但您必须小心,确保它们不会妨碍同一进程里的其它线程。 Java 线程工具和 API看似简单。 但是,编写有效使用线程的复杂程序并不十分容易。 因为有多个线程共存在相同的内存空间中并共享相同的变量,所以您必须小心,确保您的线程不会互相干扰。 线程属性为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。 必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。 线程体所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法。 当线程产生并初始化后,实时系统调用它的run()方法。 run()方法内的代码实现所产生线程的行为,它是线程的主要部分。 线程状态附图表示了线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法。 这图并不是完整的有限状态图,但基本概括了线程中比较感兴趣和普遍的方面。 以下讨论有关线程生命周期以此为据。 ●新线程态(New Thread)产生一个Thread对象就生成一个新线程。 当线程处于新线程状态时,仅仅是一个空线程对象,它还没有分配到系统资源。 因此只能启动或终止它。 任何其他操作都会引发异常。 例如,一个线程调用了new方法之后,并在调用start方法之前的处于新线程状态,可以调用start和stop方法。 ●可运行态(Runnable)start()方法产生运行线程所必须的资源,调度线程执行,并且调用线程的run()方法。 在这时线程处于可运行态。 该状态不称为运行态是因为这时的线程并不总是一直占用处理机。 特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理 机。 Java通过调度来实现多线程对处理机的共享。 注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还有优先级和调度问题。 ●阻塞/非运行态(Not Runnable)当以下事件发生时,线程进入非运行态。 ①suspend()方法被调用;②sleep()方法被调用;③线程使用wait()来等待条件变量;④线程处于I/O请求的等待。 ●死亡态(Dead)当run()方法返回,或别的线程调用stop()方法,线程进入死亡态。 通常Applet使用它的stop()方法来终止它产生的所有线程。 线程的本操作:派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。 阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。 激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。 调度(schedule):选择一个就绪线程进入执行状态。 结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。 图2 线程的状态与操作线程的另一个执行特性是同步。 线程中所使用的同步控制机制与进程中所使用的同步控制机制相同。 线程优先级虽然我们说线程是并发运行的。 然而事实常常并非如此。 正如前面谈到的,当系统中只有一个CPU时,以某种顺序在单CPU情况下执行多线程被称为调度(scheduling)。 Java采用的是一种简单、固定的调度法,即固定优先级调度。 这种算法是根据处于可运行态线程的相对优先级来实行调度。 当线程产生时,它继承原线程的优先级。 在需要时可对优先级进行修改。 在任何时刻,如果有多条线程等待运行,系统选择优先级最高的可运行线程运行。 只有当它停止、自动放弃、或由于某种原因成为非运行态低优先级的线程才能运行。 如果两个线程具有相同的优先级,它们将被交替地运行。 Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行。 一个应用程序可以通过使用线程中的方法setPriority(int),来设置线程的优先级大小。 有线程进入了就绪状态,需要有线程调度程序来决定何时执行,根据优先级来调度。 线程中的join()可以用来邀请其他线程先执行(示例代码如下);publicclassJoin01implementsRunnable{publicstaticvoidmain(String[]args){for(inti=0;i<20;i++){if(i==5){Join01j=newJoin01();Threadt=newThread(j);(被邀请先执行的线程.);();try{//邀请这个线程,先执行();}catch(InterruptedExceptione){();}}(没被邀请的线程。 +(i+1));}}publicvoidrun(){for(inti=0;i<10;i++){(()()+(i+1));}}}yield()告诉系统把自己的CPU时间让掉,让其他线程或者自己运行,示例代码如下;publicclassYield01{publicstaticvoidmain(String[]args){YieldFirstyf=newYieldFirst();YieldSecondys=newYieldSecond();YieldThirdyt=newYieldThird();();();();}}classYieldFirstextendsThread{@Overridepublicvoidrun(){for(inti=0;i<10;i++){(第一个线程第+(i+1)+次运行.);//让当前线程暂停yield();}}}classYieldSecondextendsThread{@Overridepublicvoidrun(){for(inti=0;i<10;i++){(第二个线程第+(i+1)+次运行.);//让当前线程暂停yield(); ,仅适用于Sun Solaris操作系统。 所以UNIX International线程也常被俗称为Solaris线程。 1.创建线程intthr_create(void*stack_base,size_tstack_size,void*(*start_routine)(void*),void*arg,longflags,thread_t*new_thr);2.等待线程intthr_join(thread_twait_for,thread_t*dead,void**status);3.挂起线程intthr_suspend(thread_tthr);4.继续线程intthr_continue(thread_tthr);5.退出线程voidthr_exit(void*status);6.返回当前线程的线程标识符thread_tthr_self(void);POSIX线程POSIX线程(Pthreads)的头文件是,适用于类Unix操作系统。 Windows操作系统并没有对POSIX线程提供原生的支持库。 不过Win32的POSIX线程库的一些实现也还是有的,例如pthreads-w32 。 1.创建线程intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);2.等待线程intpthread_join(pthread_tthread,void**retval);3.退出线程voidpthread_exit(void*retval);4.返回当前线程的线程标识符pthread_tpthread_self(void);5.线程取消intpthread_cancel(pthread_tthread);Win32线程Win32线程的头文件是,适用于Windows操作系统。 1.创建线程HANDLEWINAPICreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,SIZE_TdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,LPDWORDlpThreadId);2.结束本线程VOIDWINAPIExitThread(DWORDdwExitCode);3.挂起指定的线程DWORDWINAPISuspendThread(HANDLEhThread);4.恢复指定线程运行DWORDWINAPIResumeThread(HANDLEhThread);5.等待线程运行完毕DWORDWINAPIWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);6.返回当前线程的线程标识符DWORDWINAPIGetCurrentThreadId(void);7.返回当前线程的线程句柄HANDLEWINAPIGetCurrentThread(void);C++ 11 线程C++ 11 线程的头文件是。 创建线程std::thread::thread(Function&& f, Args&&... args); 等待线程结束std::thread::join(); 脱离线程控制std::thread::detach(); 交换线程std::thread::swap( thread& other ); C 11 线程C11线程的头文件是。 C11线程仅仅是个“建议标准”,也就是说100%遵守C11标准的C编译器是可以不支持C11线程的。 根据C11标准的规定,只要编译器预定义了__STDC_NO_THREADS__宏,就可以没有头文件,自然也就也没有下列函数。 1.创建线程intthrd_create(thrd_t*thr,thrd_start_tfunc,void*arg);2.结束本线程_Noreturnvoidthrd_exit(intres);3.等待线程运行完毕intthrd_join(thrd_tthr,int*res);4.返回当前线程的线程标识符thrd_tthrd_current();Java线程1)最简单的情况是,Thread/Runnable的run()方法运行完毕,自行终止。 2)对于更复杂的情况,比如有循环,则可以增加终止标记变量和任务终止的检查点。 3)最常见的情况,也是为了解决阻塞不能执行检查点的问题,用中断来结束线程,但中断只是请求,并不能完全保证线程被终止,需要执行线程协同处理。 4)IO阻塞和等锁情况下需要通过特殊方式进行处理。 5)使用Future类的cancel()方法调用。 6)调用线程池执行器的shutdown()和shutdownNow()方法。 7)守护线程会在非守护线程都结束时自动终止。 8)Thread的stop()方法,但已不推荐使用。 线程的组成1)一组代表处理器状态的CPU寄存器中的内容2)两个栈,一个用于当线程在内核模式下执行的时候,另一个用于线程在用户模式下执行的时候3)一个被称为线程局部存储器(TLS,thread-local storage)的私有储存区域,各个子系统、运行库和DLL都会用到该储存区域4)一个被称为线程ID(thread ID,线程标识符)的唯一标识符(在内部也被称为客户ID——进程ID和线程ID是在同一个名字空间中生产的,所以它们永远 不会重叠)5)有时候线程也有它们自己的安全环境,如果多线程服务器应用程序要模仿其客户的安全环境,则往往可以利用线程的安全环境

什么是线程数量

一、线程的概念一般来说,我们把正在计算机中执行的程序叫做进程(Process) ,而不将其称为程序(Program)。 所谓线程(Thread),是进程中某个单一顺序的控制流。 新兴的操作系统,如Mac,Windows NT,Windows 95等,大多采用多线程的概念,把线程视为基本执行单位。 线程也是Java中的相当重要的组成部分之一。 甚至最简单的Applet也是由多个线程来完成的。 在Java中,任何一个Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)绘图与事件处理线程调用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()——是由执行该Applet的应用调用的。 单线程的概念没有什么新的地方,真正有趣的是在一个程序中同时使用多个线程来完成不同的任务。 某些地方用轻量进程(Lightweig ht Process)来代替线程,线程与真正进程的相似性在于它们都是单一顺序控制流。 然而线程被认为轻量是由于它运行于整个程序的上下文内,能使用整个程序共有的资源和程序环境。 作为单一顺序控制流,在运行的程序内线程必须拥有一些资源作为必要的开销。 例如,必须有执行堆栈和程序计数器。 在线程内执行的代码只在它的上下文中起作用,因此某些地方用执行上下文来代替线程。 二、线程属性为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。 必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。 (1)线程体所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法。 当线程产生并初始化后,实时系统调用它的run()方法。 run()方法内的代码实现所产生线程的行为,它是线程的主要部分。 (2)线程状态附图表示了线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法。 这图并不是完整的有限状态图,但基本概括了线程中比较感兴趣和普遍的方面。 以下讨论有关线程生命周期以此为据。 ●新线程态(New Thread)产生一个Thread对象就生成一个新线程。 当线程处于新线程状态时,仅仅是一个空线程对象,它还没有分配到系统资源。 因此只能启动或终止它。 任何其他操作都会引发异常。 ●可运行态(Runnable)start()方法产生运行线程所必须的资源,调度线程执行,并且调用线程的run()方法。 在这时线程处于可运行态。 该状态不称为运行态是因为这时的线程并不总是一直占用处理机。 特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理 机。 Java通过调度来实现多线程对处理机的共享。 ●非运行态(Not Runnable)当以下事件发生时,线程进入非运行态。 ①suspend()方法被调用;②sleep()方法被调用;③线程使用wait()来等待条件变量;④线程处于I/O等待。 ●死亡态(Dead)当run()方法返回,或别的线程调用stop()方法,线程进入死亡态 。 通常Applet使用它的stop()方法来终止它产生的所有线程。 (3)线程优先级虽然我们说线程是并发运行的。 然而事实常常并非如此。 正如前面谈到的,当系统中只有一个CPU时,以某种顺序在单CPU情况下执行多线程被称为调度(scheduling)。 Java采用的是一种简单、固定的调度法,即固定优先级调度。 这种算法是根据处于可运行态线程的相对优先级来实行调度。 当线程产生时,它继承原线程的优先级。 在需要时可对优先级进行修改。 在任何时刻,如果有多条线程等待运行,系统选择优先级最高的可运行线程运行。 只有当它停止、自动放弃、或由于某种原因成为非运行态低优先级的线程才能运行。 如果两个线程具有相同的优先级,它们将被交替地运行。 Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行。 (4)幽灵线程任何一个Java线程都能成为幽灵线程。 它是作为运行于同一个进程内的对象和线程的服务提供者。 例如,HotJava浏览器有一个称为 后台图片阅读器的幽灵线程,它为需要图片的对象和线程从文件系统或网络读入图片。 幽灵线程是应用中典型的独立线程。 它为同一应用中的其他对象和线程提供服务。 幽灵线程的run()方法一般都是无限循环,等待服务请求。 (5)线程组每个Java线程都是某个线程组的成员。 线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行整体操作。 譬如,你能用一个方法调用来启动或挂起组内的所有线程。 Java线程组由ThreadGroup类实现。 当线程产生时,可以指定线程组或由实时系统将其放入某个缺省的线程组内。 线程只能属于一个线程组,并且当线程产生后不能改变它所属的线程组。 三、多线程程序对于多线程的好处这就不多说了。 但是,它同样也带来了某些新的麻烦。 只要在设计程序时特别小心留意,克服这些麻烦并不算太困难。 (1)同步线程许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态。 这就需要同步机制。 在Java中每个对象都有一把锁与之对应。 但Java不提供单独的lock和unlock操作。 它由高层的结构隐式实现, 来保证操作的对应。 (然而,我们注意到Java虚拟机提供单独的monito renter和monitorexit指令来实现lock和unlock操作。 )synchronized语句计算一个对象引用,试图对该对象完成锁操作, 并且在完成锁操作前停止处理。 当锁操作完成synchronized语句体得到执行。 当语句体执行完毕(无论正常或异常),解锁操作自动完成。 作为面向对象的语言,synchronized经常与方法连用。 一种比较好的办法是,如果某个变量由一个线程赋值并由别的线程引用或赋值,那么所有对该变量的访问都必须在某个synchromized语句或synchronized方法内。 现在假设一种情况:线程1与线程2都要访问某个数据区,并且要求线程1的访问先于线程2, 则这时仅用synchronized是不能解决问题的。 这在Unix或Windows NT中可用Simaphore来实现。 而Java并不提供。 在Java中提供的是wait()和notify()机制。 使用如下:synchronized method-1(…){ call by thread 1.‖access data area;available=true;notify()}synchronized method-2(…){‖call by thread (!available)try{wait();‖wait for notify().}catch (Interrupted Exception e){}‖access data area}其中available是类成员变量,置初值为false。 如果在method-2中检查available为假,则调用wait()。 wait()的作用是使线程2进入非运行态,并且解锁。 在这种情况下,method-1可以被线程1调用。 当执行notify()后。 线程2由非运行态转变为可运行态。 当method-1调用返回后。 线程2可重新对该对象加锁,加锁成功后执行wait()返回后的指令。 这种机制也能适用于其他更复杂的情况。 (2)死锁如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。 系统均衡是指每个线程在执行过程中都能充分访问有限的资源。 系统中没有饿死和死锁的线程。 Java并不提供对死锁的检测机制。 对大多数的Java程序员来说防止死锁是一种较好的选择。 最简单的防止死锁的方法是对竞争的资源引入序号,如果一个线程需要几个资源,那么它必须先得到小序号的资源,再申请大序号的资源。 四、线程和进程的比较进程是资源分配的基本单位。 所有与该进程有关的资源,都被记录在进程控制块PCB中。 以表示该进程拥有这些资源或正在使用它们。 另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。 与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。 当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。 线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。 寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。 发生进程切换与发生线程切换时相比较,进程切换时涉及到有关资源指针的保存以及地址空间的变化等问题;线程切换时,由于同不进程内的线程共享资源和地址 空间,将不涉及资源信息的保存和地址变化问题,从而减少了操作系统的开销时间。 而且,进程的调度与切换都是由操作系统内核完成,而线程则既可由操作系统内 核完成,也可由用户程序进行。 五、线程的适用范围典型的应用 1.服务器中的文件管理或通信控制 2.前后台处理 3.异步处理六、线程的执行特性一个线程必须处于如下四种可能的状态之一:初始态:一个线程调用了new方法之后,并在调用start方法之前的所处状态。 在初始态中,可以调用start和stop方法。 Runnable:一旦线程调用了start 方法,线程就转到Runnable 状态,注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还有优先级和调度问题。 阻塞/ NonRunnable:线程处于阻塞/NonRunnable状态,这是由两种可能性造成的:要么是因挂起而暂停的,要么是由于某些原因而阻塞的,例如包括等待IO请求的完成。 退出:线程转到退出状态,这有两种可能性,要么是run方法执行结束,要么是调用了stop方法。 最后一个概念就是线程的优先级,线程可以设定优先级,高优先级的线程可以安排在低优先级线程之前完成。 一个应用程序可以通过使用线程中的方法setPriority(int),来设置线程的优先级大小。 线程有5种基本操作: 派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。 阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。 激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。 调度(schedule):选择一个就绪线程进入执行状态。 结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。 七、线程的分类线程有两个基本类型: 用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。 系统级线程(核心级线程):由操作系统内核进行管理。 操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行、撤消线程。 附:线程举例1. SUN Solaris 2.3Solaris支持内核线程、轻权进程和用户线程。 一个进程可有大量用户线程;大量用户线程复用少量的轻权进程,轻权进程与内核线程一一对应。 用户级线程在调用核心服务时(如文件读写),需要“捆绑(bound)”在一个LWP上。 永久捆绑(一个LWP固定被一个用户级线程占用,该LWP移到LWP池之外)和临时捆绑(从LWP池中临时分配一个未被占用的LWP)。 在调用系统服务时,如果所有LWP已被其他用户级线程所占用(捆绑),则该线程阻塞直到有可用的LWP。 如果LWP执行系统线程时阻塞(如read()调用),则当前捆绑在LWP上的用户级线程也阻塞。 ¨有关的C库函数/* 创建用户级线程 */int thr_create(void *stack_base, size_t stack_size,void *(*start_routine)(void *), void *arg, long flags,thread_t *new_thread_id);其中flags包括:THR_BOUND(永久捆绑), THR_NEW_LWP(创建新LWP放入LWP池),若两者同时指定则创建两个新LWP,一个永久捆绑而另一个放入LWP池。 ²有关的系统调用 /* 在当前进程中创建LWP */int _lwp_create(ucontext_t *contextp, unsigned long flags,lwpid_t *new_lwp_id);/* 构造LWP上下文*/void _lwp_makecontext(ucontext_t *ucp, void (*start_routine)( void *), void *arg,void *private, caddr_t stack_base, size_t stack_size);/* 注意:没有进行“捆绑”操作的系统调用 */2. Windows NTNT线程的上下文包括:寄存器、核心栈、线程环境块和用户栈。 NT线程状态 (1) 就绪状态:进程已获得除处理机外的所需资源,等待执行。 (2) 备用状态:特定处理器的执行对象,系统中每个处理器上只能有一个处于备用状态的线程。 (3) 运行状态:完成描述表切换,线程进入运行状态,直到内核抢先、时间片用完、线程终止或进行等待状态。 (4) 等待状态:线程等待对象句柄,以同步它的执行。 等待结束时,根据优先级进入运行、就绪状态。 (5) 转换状态:线程在准备执行而其内核堆栈处于外存时,线程进入转换状态;当其内核堆栈调回内存,线程进入就绪状态。 (6) 终止状态:线程执行完就进入终止状态;如执行体有一指向线程对象的指针,可将线程对象重新初始化,并再次使用。 NT线程的有关APICreateThread()函数在调用进程的地址空间上创建一个线程,以执行指定的函数;返回值为所创建线程的句柄。 ExitThread()函数用于结束本线程。 SuspendThread()函数用于挂起指定的线程。 ResumeThread()函数递减指定线程的挂起计数,挂起计数为0时,线程恢复执行。



相关标签: 超越实现高效的线程管理提升多线程提升Java的典型事件JavasuspendThread

上一篇:Leaflet轻松构建互动地图的开源JavaScript

下一篇:避免死锁了解suspendThread的风险并寻找替

内容声明:

1、本站收录的内容来源于大数据收集,版权归原网站所有!
2、本站收录的内容若侵害到您的利益,请联系我们进行删除处理!
3、本站不接受违法信息,如您发现违法内容,请联系我们进行举报处理!
4、本文地址:http://www.jujiwang.com/article/b13e50c6f448876b4db8.html,复制请保留版权链接!


温馨小提示:在您的网站做上本站友情链接,访问一次即可自动收录并自动排在本站第一位!
随机文章
多项式拟合在机器学习和人工智能中的应用:从理论到实践 (多项式拟合在线)

多项式拟合在机器学习和人工智能中的应用:从理论到实践 (多项式拟合在线)

简介多项式拟合是一种机器学习技术,用于拟合数据点到多项式曲线,它是一种强大的工具,可以用于各种应用,包括回归分析、曲线拟合和预测,在本文中,我们将探讨多项式拟合在机器学习和人工智能中的应用,我们将从理论基础开始,然后讨论实际应用以及使用多项式拟合的代码示例,理论基础多项式拟合的目标是找到一个多项式函数,它最能拟合一组数据点,对于给定的...。

本站公告 2024-09-29 19:16:44

通过磁盘配额实施自动化存储管理,节省管理员时间 (磁盘配额在哪里)

通过磁盘配额实施自动化存储管理,节省管理员时间 (磁盘配额在哪里)

随着企业数据不断增长,管理和优化存储空间变得越来越具有挑战性,磁盘配额是一种有用的工具,可以通过自动化存储管理流程,为管理员节省大量时间和精力,什么是磁盘配额,磁盘配额是指为指定用户或组分配的存储空间限制,通过实施磁盘配额,管理员可以限制特定用户或组可以使用的存储量,这有助于防止单个用户或组垄断共享存储池,并确保所有用户公平地访问存储...。

技术教程 2024-09-25 12:06:29

探索 Delphi XE5 的强大功能,打造卓越的桌面、移动和 Web 应用程序 (探索deplore)

探索 Delphi XE5 的强大功能,打造卓越的桌面、移动和 Web 应用程序 (探索deplore)

DelphiXE5是一款用于构建桌面、移动和网络应用程序的强大开发工具,它提供了丰富的功能和特性,使开发人员能够创建高性能、可扩展且易于维护的应用程序,桌面应用程序DelphiXE5非常适合构建功能丰富且用户友好的桌面应用程序,它支持各种控件和组件,包括按钮、文本框、网格和图表,开发人员可以使用直观的拖放界面轻松地创建复杂的应用程序,...。

互联网资讯 2024-09-24 09:53:13

网页播放器代码的终极指南:成为多媒体大师 (网页播放v)

网页播放器代码的终极指南:成为多媒体大师 (网页播放v)

前言在现代web开发中,多媒体内容扮演着越来越重要的角色,从视频和音频到交互式3D模型,丰富的多媒体体验可以显著提升用户参与度和网站整体质量,为了在网页上实现这些多媒体内容,网页播放器发挥着至关重要的作用,网页播放器是一个包含用户界面和媒体播放功能的嵌入式组件,允许用户轻松控制和观看媒体内容,常见的网页播放器技术HTML5媒体元素HT...。

技术教程 2024-09-15 17:17:18

深入了解 fprintf:C 中的格式化输出神器 (深入了解法律)

深入了解 fprintf:C 中的格式化输出神器 (深入了解法律)

简介fprintf函数是C编程语言中用于格式化输出的强大工具,它允许您以一种可控的方式将数据写入文件或标准输出流,通过使用格式化字符串,您可以指定输出数据的特定格式,例如对齐、宽度和精度,语法fprintf函数的语法如下,```cintfprintf,FILEstream,constcharformat,...,```其中,`str...。

互联网资讯 2024-09-15 11:20:39

掌握数据库三范式:创建高效且可靠的数据库 (掌握数据库三种方式)

掌握数据库三范式:创建高效且可靠的数据库 (掌握数据库三种方式)

掌握数据库三范式,创建高效且可靠的数据库、灵活性和安全性,虽然可能会有一些注意事项,但三范式仍然是设计健壮和可靠的关系数据库的基础,...。

本站公告 2024-09-13 10:29:40

PHP最佳实践:编写干净、可维护且高效的代码 (php实战)

PHP最佳实践:编写干净、可维护且高效的代码 (php实战)

作为一名PHP开发者,实现编写干净、可维护且高效的代码至关重要,这不仅可以提高你的生产力,还可以帮助你构建更健壮、更可靠的应用程序,本文将介绍一些PHP最佳实践,帮助你提高代码质量,这些实践涵盖了编码风格、错误处理、性能优化和安全性等各个方面,编码风格保持一致的编码风格对于提高代码可读性和可维护性至关重要,以下是一些编码风格最佳实践,...。

互联网资讯 2024-09-12 14:39:38

数据存储优化:提高应用程序性能和降低成本 (数据存储优化包括)

数据存储优化:提高应用程序性能和降低成本 (数据存储优化包括)

数据存储优化对于提高应用程序性能和降低成本至关重要,通过优化数据存储,可以减少应用程序的响应时间、提高吞吐量并降低存储成本,数据存储优化包括,选择正确的存储类型,根据应用程序的工作负载和性能要求,选择适当的存储类型,例如块存储、文件存储或对象存储,优化数据结构,设计数据结构以减少冗余和提高查询性能,例如使用索引和表分区,实现缓存,将常...。

互联网资讯 2024-09-11 13:20:22

掌握 Flash 源码:为您的网站和应用程序注入交互性和视觉吸引力 (掌握英语)

掌握 Flash 源码:为您的网站和应用程序注入交互性和视觉吸引力 (掌握英语)

引言Flash,一种曾经无所不在的交互式多媒体平台,近年来已被HTML5等更新技术所超越,Flash源码仍然是为网站和应用程序创建引人注目的交互性和视觉效果的宝贵工具,本指南将为您提供掌握Flash源码所需的知识和技能,从而提升您的网站和应用程序,我们将探讨Flash源码的基础知识、各种工具,以及创建令人惊叹的交互式体验的最佳实践,什...。

本站公告 2024-09-10 14:54:19

基于云的解决方案,无需本地安装或维护 (基于云的解决方案有哪些)

基于云的解决方案,无需本地安装或维护 (基于云的解决方案有哪些)

随着云计算的蓬勃发展,越来越多的企业和个人选择基于云的解决方案来满足他们的计算和存储需求,与传统的本地软件相比,基于云的解决方案提供了许多优势,其中一个主要优势就是无需本地安装或维护,这带来的好处包括,降低成本,无需购买和维护本地硬件,从而降低了前期成本和持续成本,提高可扩展性,云平台可以轻松地按需纵向扩展或缩小,以适应业务需求的变化...。

互联网资讯 2024-09-09 18:01:21

使结账过程复杂和耗时。(结账后才能进行的操作是)

使结账过程复杂和耗时。(结账后才能进行的操作是)

结账是购买过程中的关键一步,也是许多企业面临的挑战,复杂且耗时的结账流程可能会让客户望而却步,导致销售损失,为了避免这种情况,企业可以采取多种措施来简化结账流程,提高客户满意度,在本文中,我们将探讨结账后才能进行的常见操作以及这些操作如何影响客户体验,我们还将提供一些提示,帮助企业简化结账流程,结账后才能进行的常见操作以下是一些常见的...。

互联网资讯 2024-09-06 08:40:24

自己如何做网站 网站建设教程分享 (自己如何做网站建设)

自己如何做网站 网站建设教程分享 (自己如何做网站建设)

由于现在网站建设的门槛是比较低的,做网站变得非常容易,小企业甚至个人都可以利用各种自助网站建设系统来建设自己的网站,拥有自己的网站,便于宣传、展示企业形象、推广产品和品牌营销,那么自己如何做网站,步骤是什么,其实很简单,只要做以下步骤,一、下载自助建站系统目前市场上自助建站系统很多,但是并不是说所有的质量都是好的,我们需要仔细的进行辨...。

技术教程 2024-09-02 02:33:49