一、线程
1>创建新线程
a>自定义类并实现Runnable接口,实现run()方法。
b >创建Thread子类的实例,用实现Runnable接口的对象作为参数实例化该Thread对象。
c>调用Thread的start()方法来启动该线程。
使用Thread t = new Thread(Runnable target);将创建一个线程。
注:不要调用Thread类或Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用Thread.start方法,这个方法将创建一个执行run方法的新线程。
2>中断线程
a>当线程的run方法执行方法体中最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止。在Java的早期版本中,还有一个stop方法,其它线程可以调用它终止程序。但是,这个方法现在已经被弃用了。
b>使用Interrupt方法可以用来请求终止线程,当对线程调用interrupt方法时,线程的中断状态将被置位。这是每一个线程都具有的boolean状态。每个线程都应该不时地检查这个标志,以判断线程是否被中断。
c>判断中断状态是否被置位,使用如下语句:
while(!Thread.currentThread().isInterrupted()&& more work to do){ do more work }
如果线程被阻塞,就无法检测中断状态,这是产生InterruptedException异常的地方。当在被阻塞的线程(调用sleep或wait)上调用interrupt方法时,阻塞调用将会被Interrupted Exception异常中断。
如果每次迭代之后都调用sleep方法(或者其他可中断的方法),isInterrupted检测就没必要也没用处了,如果在中断状态被置位时调用sleep方法,它不会休眠反而会清除这一状态并抛出InterruptedException。所以如果在循环中调用sleep,不要去检测中断状态,只需捕获InterruptedException。
在很多发布的代码中会发现InterruptedException被抑制在很低的层次上:
void mySubTask(){ ... try{sleep(delay);} catch(InterruptedException e){} ... }
不要这样做,如果不认为catch中做一处理有什么好处的话,有两种合理的选择:
i>在catch中调用Thread.currentThread().interrupt()来设置中断状态。调用者可以对其进行检测。
void mySubTask(){ ... try{sleep(delay);} catch(InterruptedException e){Thread.currentThread().interrupted();} ... }
ii>更好的选择用throw InterruptedException标记你的方法,不采用try语句块来捕获已成。这样调用者可以捕获这个异常:
void mySubTask() throws InterruptedException { ... sleep(delay); ... }
3>线程状态
线程可以有如下6种状态:
a>New(新创建)
当用new操作符创建一个新线程时,该线程还没有开始运行。这意味着它的状态是new。
b>Runnable(可运行)
一旦调用start方法,线程处于runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。(Java的规范说明没有将它作为一个单独状态。一个正在运行中的线程仍然处于可运行状态。)
一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他的线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。当选择下一个线程时,操作系统考虑线程的优先级。
注:现在所有的桌面以及服务器操作系统都使用抢占式调度。但是,像手机这样的小型设备可能使用协作式调度。在这样的设备中,一个线程只有在调用yield方法、或者被阻塞或等待时,线程才失去控制权。
在具有多个处理器的机器上,每个处理器运行一个线程,可以有多个线程并行运行。当然,如果线程的数目多于处理器的数目,调度器依然采用时间片机制。
c>Blocked(被阻塞)
d>Waiting(等待)
e>Timed waiting(计时等待)
当线程处于被阻塞或等待状态时,它暂时不活动。它不允许任何代码且消耗最少的资源。直到线程调度器重新激活它。
f>Terminated(被终止)
线程因如下两个原因之一而被终止:
i>因为run方法正常退出而自然死亡。
ii>因为一个没有捕获的异常终止了run方法而意外死亡。
特别是,可以调用线程的stop方法杀死一个线程。该方法抛出ThreadDeath错误对象,由此杀死线程。但是,stop方法已过时,不要在代码中使用。
常用方法:
join()方法等待终止指定的线程。
4>线程属性
a>线程优先级
在Java程序设计语言中,每一个线程有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用setPriority方法设置线程的优先级。
注:每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。
不要将程序构建为功能的正确性依赖于优先级。
常用方法:yield()导致当前线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。这是一个静态方法。
b>守护线程
t.setDaemon(true);
将线程转换为守护线程(daemon thread)。守护线程的唯一用途是为其他线程提供服务。当只剩下守护线程时,虚拟机将退出。
注:守护线程应该永远不去访问固有资源,如:文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
c>未捕获异常处理器
线程的run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止。在这种情况下,线程就死亡了。
但是,不需要任何catch子句来处理可以被传播的异常。相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。
该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法。
void uncaughtException(Thread t,Throwable e)
i>可以用setUncaughtExceptionHandler方法为任何线程安装一个处理类。
ii>也可以用Thread类的静态方法setDefaultUncaughtExceptionHandler为所有的线程安装一个默认的处理器。替换处理器可以使用日志API发送未捕获异常的报告到日志文件。
注:如果不安装默认的处理器,默认的处理器为空。但是,如果不为独立的线程安装处理器,此时的处理器就是该线程的ThreadGroup对象。
线程组是一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组,但是,也可能会建立其他的组。现在引入了更好的特性用于线程集合的操作,所以建议不要在程序中使用线程组。
ThreadGroup类实现Thread.uncaughtExceptionHandler接口。它的uncaughtException方法如下操作:
i>如果该线程组有父线程组,那么父线程组的uncaughtException方法被调用。
ii>否则,如果Thread.getDefaultExceptionHandler方法返回一个非空的处理器,则调用该处理器。
iii>否则,如果Throwable是ThreadDeath的一个实例,什么都不做。
iv>否则,线程的名字以及Throwable的栈踪迹被输出到System.err上。