Java线程生命周期

线程在Java中占据着重要的位置,同时包含着很多的内容,从本文到同步、线程消息通知、线程安全集合类,再到线程池、线程调度,以及线程与I/O之间的关系等等,都值得认真研究一番。本文尝试归纳线程的生命周期,从线程的创建到线程管理这些基本内容。

重要方法

以下是线程生命周期中一些重要的方法,其中有些是已经过时,不建议使用的方法。

1
2
3
4
5
6
7
8
9
10
11
12
public static native void sleep(long millis) throws InterruptedException;
public synchronized void start()
public void run()
public void interrupt()
public boolean isInterrupted()
public final native boolean isAlive();
public final void join()
//废弃方法,过时方法
public final void stop()
public final void suspend()
public final void resume()

线程创建

继承Thread类

1
2
3
4
5
6
7
8
9
10
11
12
public class ScheduleTask extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
ScheduleTask t1 = new ScheduleTask();
ScheduleTask t2 = new ScheduleTask();
t1.start();
t2.start();
}
}

实现Runnable接口

这里用了一个经典的卖票的例子,但却是线程不安全的,因为自增或者自减操作本身不是原子性的,一旦出现线程竞争就会导致数据不一致,比如下面两个线程分别卖完票之后,居然都出现了剩余99张票的情况,这就分明是错误的。如果要想修改的话可以将int类型修改为AtomicInteger原子类或者用synchronized这个关键字修饰自减整个操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class SellTicket implements Runnable {
//线程不安全的反例,因为每个卖票操作应该是原子的
private int tickets=100;//AtomicInteger
@Override
public void run() {
while (tickets>0) {
System.out.println(Thread.currentThread().getName()+" say :The tickets is remaining " + --tickets);
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread seller1 = new Thread(sellTicket);
Thread seller2 =new Thread(sellTicket);
seller1.start();
seller2.start();
}
/**
* 输出:
* Thread-1 say :The tickets is remaining 99
* Thread-0 say :The tickets is remaining 99
* ...
*/
}

线程启动

当准备要运行程序代码时候,就调用start方法,这个方法会做一些准备工作之后调用线程内部的run方法。在start()方法被调用之后,新的thread可说是“活跃的(alive)”,可以通过isAlive()方法判断线程的状态,如果返回结果是true,则该线程已经被启动并且正在运行run()这个方法,否则该线程可能还没有启动或者已经被终结了。

线程终止

一且启动后,thread只能执行一个method;run()方法。这个method可以很复杂,它
可以永远地执行下去,且它也可以调用其他method.无论如何,一旦run()方法完成
了执行动作,thread也就必须完成它的执行动作。因此,终结thread的唯一办法就是安排run()方法完成。

  • 线程的stop()方法,但是stop()方法有个固有的问题(内部的race condition),因此stop()方法已经被废除且不应该使用。
  • 设定标记,通过判断外界标记条件是否符合要求来终止线程运行。
  • interrupt()方法,当你想终结thread的时候,通常会想立即地完成它的blocking method,此时可以使用Thread的interrupt()方法来中断任何的blocking method.但是此方法会带来一个问题就是,中断任何的blocked method 都会抛出InterruptedException.比如sleep()wait()join()方法等等,都是blocking method.
  • 异常造成的终止,run()这个method不能抛出checked异常,但如同其他的Java method,它可以抛出unchecked异常。(这里简单说下:Checked exception 是在编译阶段的异常,强制检查,必须要catch的,比如IOException;unchecked exception分为Error和Runtime exception,是 runtime 阶段碰到的异常. 在编译的时候不需要检查 (checked),也无法检查的,比如NPE、ArrayIndexOutOfBoundsException等等.)如果run方法在运行时不能catch到运行时异常,也会造成线程的终止。

线程善后

一个完成run()方法的线程就是已经终结的,不再是活跃的(isAlive()方法会返回false)。一般来说,之后你就不应该再占据该thread的引用以便让该thread在终结时能够被同收。一个还要占据thread引用的场景就是要判别该线程是否已经完成工作,通过join()方法来完成。
join()方法经常用在想知道是否该线程已经完成,当调用该方法时,join()方法会被block住直到thread完成它的run()方法。如果thread已经完成它的
run()方法,join()方法就会立即返回,不再block。

-EOF-