【codejava】第八版:第十四章 多线程[001] [20180105]

14.1 线程的概念

    读者可能已经很熟悉操作系统中的多任务(multitasking):在同一刻运行多个程序的能力。例如,在编辑或下载邮件的同时可以打印文件。今天,人们很可能有单台拥有多个CPU的计算机,但是,并发执行的进程数并不是有CPU数目制约的。操作系统将CPU的时间片分配给每一个进程,给人并行处理的感觉

    多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务。通常,每一个任务称为一个线程(thread),它是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序(multithreaded)。
    然而,多线程跟多进程有哪些区别呢本质的区别在于每个进程拥有自己的一整套变量,而线程则共享数据。共享变量使线程之间的通信比进程之间的通信更有效,更容易。此外,在有些操作系统中,与进程相比较,线程更“轻量级”,创建,撤销一个线程比启动新进程的开销要小得多。

14.2 中断线程
   当线程的run方法执行方法体中的最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止。在java早期版本中,还有一个stop方法,其他的线程可以调用它终止线程。但是,这个方法现在已经被弃用了。稍后讨论“为什么stop方法和suspend方法遭到弃用?”的缘由。
    有一种可以强制线程终止的方法。然而,interrupt方法可以用来请求终止线程。
    当对一个线程调用interrput方法时,线程的中断状态将被置位。这是每一个线程都具有的boolean标志。每个线程都应该不时地检查这个标志,以判断线程是否被中断。要想弄清中断状态是否被置位,通过下列方法来进行查看。
Thread.currentThread().isInterrupted();
      但是,如果线程被阻塞,就无法检测中断状态。这是产生InterruptedException异常的地方。当在一个被阻塞的线程(调用sleep或wait)上调用interrupt方法时,阻塞调用将会被 InterruptedException异常中断。(存在不能被中断的阻塞I/O调用,应该考虑选择可中断的调用。有关细节请参看卷2的第一章和第三章)。
     没有任何语言方面的需求要求一个被中断的线程应该终止。中断一个线程不过是引起它的注意,被中断的线程可以决定如何响应中断。某些线程是如此重要以至于应该处理完异常后,继续执行,而不理会中断,但是更普遍的情况是,线程将简单地中断作为一个终止的请求。


  14.3 线程状态

   线程可以有如下6种状态:

    ·New(新生)     ·Runnable(可运行)      ·Blocked(被阻塞)      ·Waiting(等待)      ·Timed waiting(计时等待)      ·Terminated(被终止)
      要确定一个线程的当前状态,可以调用getState方法

14.3.1 新生线程
    当用new操作符创建一个新线程时,如
Thread t  =  new Thread(r);
该线程还没有运行。这意味着它的状态是new。当一个线程处于新生状态时,程序还没有开始运行线程的代码。在线程运行之前还有一些簿记工作要做。

14.3.2 可运行线程
    一旦调用start方法,线程处于runnable状态。 一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间 。(java的规范说明没有将它作为一个单独状态杜。一个正在运行中的线程仍然处于可运行状态)。
    一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行的线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。当选择下一个线程时,操作系统会考虑线程的优先级——更多内容看14.4.1节。
    现在所有的桌面以及服务器操作系统都使用抢占式调度。但是,像手机这样的小型设备可能使用协作式调度,这样的设备,一个线程只有在调用yield方法,或者被阻塞或等待时,线程才会失去控制权。
    在具有多个处理器的机器上,每一个处理器运行一个线程,可以有多个线程并行运行。当让,如果线程的数目多余处理器的数目,调度器依然采用时间片机制。

14.3.3 被阻塞线程和等待线程
    当线程处于被阻塞或等待状态时,它暂时不活动。他不运行任何代码且消耗最少的资源。知道线程调度器重新激活它。细节取决于它是怎样达到非活动的状态的。
    ·当一个线程试图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程进入非阻塞状态。
    ·当线程等待另一个线程通知调度器的一个条件时,它自己进入等待状态。在调用Object.wait或Thread.join方法,或者是等待java.util.concurrent库中的Lock或Condition时,就会出现这种情况。实际上,被阻塞状态与等待状态有有很大不同的
    ·有几个方法有一个超时参数。调用它们导致线程进入计时等待(timed wating)状态。这一状态将一直保持到超时期满或者接收到适当的通知。带有超时参数的方法有Thread.sleep和Object.wait,Thread.join,Lock.tryLock以及Condition.await的计时版。

14.3.4 被终止的线程
    如下两个原因:
    ·因为run方法正常退出而自然死亡。
    ·因为一个没有捕获的异常终止了run方法而意外死亡。
    特别是,可以调用线程的stop方法杀死一个线程。该方法抛出一个ThreadDeath对象,由此杀死线程。但是stop方法已经过时,不要在自己的代码中调用它。

14.4 线程属性
下面将讨论线程的各种属性,其中包括:线程优先级,守护线程,线程组以及处理未捕获异常的处理器。
14.4.1 线程优先级
    在java程序设计语言中,每一个线程有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用setPriority方法提高或降低任何一个线程的优先级。可以将优先级设置为在MIN_PRIORITY(在Thead中定义为1)与MAX_PRIORITY(定义为10)之间的任何值。NORM_PRIORITY被定义为5.    
    每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。但是,线程优先级是高度依赖于系统的。当虚拟机依赖于宿主机平台的线程实现机制时,Java线程的优先级被映射到宿主机平台的优先级上,优先级个数也许更多,也许更少。
    例如,windows有7个优先级别,一些java优先级将映射到相同的操作系统优先级。在Sun为Linux提供的Java虚拟机,线程的优先级被忽略——所有的线程具有相同的优先级。
    初级程序员常常过度使用线程优先级。为优先级而烦恼是事出有因的。不要将程序构建为功能的正确性依赖于优先级。

×警告:如果确实要使用优先级,应该避免初学者常犯的一个错误。如果有几个高优先级的线程没有进入非活动状态,低优先级的线程可能永远也不能执行。每当调度器决定运行一个新线程时,首先会在具有高优先级的线程中进行选择,尽管这样会使低优先级的线程完全饿死。

14.4.2 守护线程
t.setDaemon(true);
    以上代码 线程转换为守护线程(Daemon thread)。这样一个线程没有什么神奇。守护线程的唯一用途是为其他的线程提供服务。计时线程就是一个例子,它定时地发送信号给其他线程或者清空过时的高速缓存项的线程。当只剩下守护线程时,虚拟机就退出了,由于如果只剩下守护线程,就没有必要运行程序了。
      守护线程应该永远不去访问固有资源,如文件,数据库,因为它会在任何时候甚至在一个操作的中间发生中断

14.4.3 未捕获异常处理器
      线程的run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止。在这种情况下,线程就死亡了。
    但是,不需要任何catch子句来处理可以被传播的异常。相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法:
  public  interface UncaughtExceptionHandler { 
         void uncaughtException(Thread t, Throwable e);
 }
    

   


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值