Java多线程之线程的创建、中断、状态与属性

494 查看

多进程与多线程的本质区别在于:每个进程拥有自己的一整套变量,而线程则共享数据。如果需要执行一个比较耗时的任务,应该使用独立的线程。

可以通过实现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 {
            输出栈踪迹到标准错误流
        }
    }
}