多进程与多线程的本质区别在于:每个进程拥有自己的一整套变量,而线程则共享数据。如果需要执行一个比较耗时的任务,应该使用独立的线程。
可以通过实现Runnable
接口或继承Thread
类来创建独立的线程。
1) 实现Ruannable
接口
class MyRunnable implements Runnable {
public void run() {
task code
}
}
Ruannable r = new MyRuannable();
Thread t = new Thread(r);
t.start();
2) 继承Thread
类
class MyThread extends Thread {
public void run() {
task code
}
}
Thread t = new MyThread();
t.start();
对于Thread
类或Runnable
对象,启动新线程调用的是start()
方法,直接调用run()
方法只会在同一个线程中执行任务。start()
会启动这个线程,引发调用run()
方法。start()
方法会立即返回,并且新线程并行运行。
如果有很多任务,为每个任务创建一个独立的线程付出的代价太大,可以使用线程池来解决这个问题。
中断线程
当线程的run()
方法方法体执行完毕(自然终止)或在方法中出现没有捕获的异常时(意外终止),线程将终止。另外,可以使用interrupt()
方法发送中断请求强制线程终止。
每个线程都有boolean
标志的中断状态位,想弄清当前线程是否被中断(中断状态位是否被置位),需要当前线程自己判断。并且,被中断的线程可以决定如何响应中断。如果被中断线程被阻塞,就无法检测中断状态,就会产生InterruptedException
异常。
public void run() {
try {
do some work
while(! Thread.currentThread().isInterrupted() && more work to do) {
do more work
}
} catch(InterruptedException e) {
// thread was interrupted during sleep or wait (in blocked)
} finally {
cleanup if required
}
}
当产生异常时,有两种处理方式选择。
1) 在catch
子句中设置中断状态。
catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
2) 不采用try
语句块捕获异常,交给调用者处理。
void mySubTask() throws InterruptedException {}
测试当前线程是否被中断有interrupted()
和isInterrupted()
两个方法。interrupted()
是一个静态方法,有副作用,会把中断状态位置为false
;isInterrupted()
是一个实例方法,无副作用。
线程状态
线程有6种状态,可以调用getState()
获取线程状态。
New:新创建状态,在可运行前还有些工作要做;
Runnable:可运行状态,可能正在运行也可能没有运行,是否运行需看操作系统调度;
Blocked:被阻塞状态,不活动,可能由于请求锁失败;
Waiting:等待状态,不活动,等待通知,比如调用
Thread.join()
方法;Timed Waiting:计时等待状态,不活动,等待超时或通知,比如调用
Thread.sleep(long millis)
或join(long millis)
方法;Terminated:被终止状态,线程可能由于自然死亡或意外死亡。
线程属性
线程优先级
每一个线程都有一个优先级,默认继承父线程。注意,不要将程序功能的正确性依赖于优先级。可以通过setPriority(int newPriority)
方法设置线程优先级,最小优先级MIN_PRIORITY
为1,默认优先级NORM_PRIORITY
为5,最高优先级MAX_PRIORITY
为10。
当操作系统的线程调度器有机会选择新线程时,会首先选择拥有较高优先级的线程。调用Thread
类的静态方法yield()
可使当前执行线程处于让步状态,如果有可运行的线程优先级大于等于此线程,那么这些线程将被调度。
守护线程
守护线程的唯一用途是为其他线程提供服务,通过调用t.setDaemon(true)
将线程转化为守护线程。如果只剩下守护线程,虚拟机就会退出。
注意,守护线程应该永远不去访问固有资源,因为这些操作可能会发生中断。
未捕获异常处理器
线程的run()
方法不能抛出任何被检测的异常,但可能会抛出不被检测的异常,导致线程终止。就在线程死亡之前,异常被传递到未捕获异常处理器,进行处理,比如使用日志API发送异常到日志文件。
class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e) {
// t - terminated thread without exception catch
// e - uncaught exception object
do something like send exception info to log file
}
}
为线程设置处理器可通过方法setUncaughtExceptionHandler()
或静态方法setDefaultUncaughtExceptionHandler()
完成。如果不设置未捕获异常处理器,那么默认处理器为空。
方法getUncaughtExceptionHandler()
用于获取未捕获异常处理器,但是有副作用,如果返回值为空,那么就会将ThreadGroup
对象设置为处理器。线程组ThreadGroup
对象的执行流大致如下:
if(该线程组有父线程组) {
父线程组的uncaughtException方法被调用
} else {
if(Thread.getDefaultExceptionHandler()返回非空处理器) {
调用该处理器
} else {
if(Throwable是ThreadDeath的一个实例) {
// 由过时方法stop产生
什么都不做
} else {
输出栈踪迹到标准错误流
}
}
}