实现线程的法子 有两种:
1. 继承java.lang.Thread,并重写它的run()法子 ,将线程的履行主体放入其中。
2. 实现java.lang.Runnable接口,实现它的run()法子 ,并将线程的履行主体放入其中。
public class ThreadTest extends Thread {
public void run() {
// 在这里编写线程履行
的主体
// do something
}
}
public class RunnableTest implements Runnable {
public void run() {
// 在这里编写线程履行
的主体
// do something
}
}
这两种实现法子 的差别并不大。继承Thread类的法子 实现起来较为简略,但是继承它的类
就不能再继承别的类了,因此也就不能继承别的类的有用的法子 了。而应用是想Runnable
接口的法子 就不存在这个问题了,而且这种实现法子 将线程主体和线程对象本身分手开来,
逻辑上也较为清楚,所以推选大家更多地采纳这种法子 。
如何启动线程
我们通过以上两种法子 实现了一个线程之后,线程的实例并没有被创立,因此它们也并没有
被运行。我们要启动一个线程,必须 调用法子 来启动它,这个法子 就是Thread类的start()方
法,而不是run()法子 (既不是我们继承Thread类重写的run()法子 ,也不是实现Runnable接
口的run()法子 )。run()法子 中包孕的是线程的主体,也就是这个线程被启动后将要运行的
代码,它跟线程的启动没有任何关系。上面两种实现线程的法子 在启动时会有所不同。
继承Thread类的启动法子 :
public class ThreadStartTest {
public static void main(String[] args) {
// 创立
一个线程实例
ThreadTest tt = new ThreadTest();
// 启动线程
tt.start();
}
}
实现Runnable接口的启动法子 :
public class RunnableStartTest {
public static void main(String[] args) {
// 创立
一个线程实例
Thread t = new Thread(new RunnableTest());
// 启动线程
t.start();
}
}
实际上这两种启动线程的法子 原理是一样的。首先都是调用本地法子 启动一个线程,其次是
在这个线程里履行目标 对象的run()法子 。那么这个目标 对象是什么呢?为了弄明白这个问
题,我们来看看Thread类的run()法子 的实现:
public void run() {
if (target != null) {
target.run();
}
}
当我们采纳实现Runnable接口的法子 来实现线程的情况 下,在调用new Thread(Runnable
target)结构器时,将实现Runnable接口的类的实例设置成了线程要履行的主体所属的目标 对
象target,当线程启动时,这个实例的 run()法子 就被履行了。当我们采纳继承Thread的方
式实现线程时,线程的这个run()法子 被重写了,所以当线程启动时,履行的是这个对象自
身的 run()法子 。总结起来就一句话,线程类有一个Runnable类型的target属性,它是线程
启动后要履行的run()法子 所属的主体,如果我们采纳的是继承Thread类的法子 ,那么这个
target就是线程对象自身,如果我们采纳的是实现Runnable接口的法子 ,那么这个target就
是实现了Runnable接口的类的实例。
线程的状态
在Java 1.4及以下的版本中,每个线程都具有新建、可运行、阻塞、逝世亡四种状态 ,但是在
Java 5.0及以上版本中,线程的状态 被扩充为新建、可运行、阻塞、等候、定时等候、逝世亡
六种。线程的状态 完整包孕了一个线程从新建到运行,最后到收场的全部生命 周期。线程状
态的具体信息如下:
1. NEW(新建状态 、初始化状态 ):线程对象已经被创立,但是还没有被启动时的状态 。
这段光阴就是在我们调用new命令之后,调用start()法子 之前。
2. RUNNABLE(可运行状态 、就绪状态 ):在我们调用了线程的start()法子 之后线程所
处的状态 。处于RUNNABLE状态 的线程在JAVA虚拟机(JVM)上是运行着的,但是它可
能还正在等候操作系统 分配给它相应的运行资源以得以运行。
3. BLOCKED(阻塞状态 、被中断 运行):线程正在等候其它的线程释放同步锁,以进
入一个同步块或者同步法子 持续运行;或者它已经进入了某个同步块或同步法子 ,在运行的
历程中它调用了某个对象继承自java.lang.Object的wait()法子 ,正在等候重新返回这个同步
块或同步法子 。
4. WAITING(等候状态 ):当前线程调用了java.lang.Object.wait()、
java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三个中的任意一个法子 ,
正在等候另外一个线程履行某个操作。比如一个线程调用了某个对象的wait()法子 ,正在等
待其它线程调用这个对象的notify() 或者notifyAll()(这两个法子 同样是继承自Object类)方
法来唤醒它;或者一个线程调用了另一个线程的join()(这个法子 属于 Thread类)法子 ,正
在等候这个法子 运行收场。
5. TIMED_WAITING(定时等候状态 ):当前线程调用了 java.lang.Object.wait(long
timeout)、java.lang.Thread.join(long
millis)、java.util.concurrent.locks.LockSupport.packNanos(long
nanos)、java.util.concurrent.locks.LockSupport.packUntil(long deadline)四个法子 中的任意一个,
进入等候状态 ,但是与WAITING状态 不同的是,它有一个最大等候光阴,即使等候的条件
仍然没有满足,只要到了这个光阴它就会主动醒来。
6. TERMINATED(逝世亡状态 、终止状态 ):线程完成履行后的状态 。线程履行完run()方
法中的整个代码,从该法子 中退出,进入TERMINATED状态 。还有一种情况 是run()在运行
历程中抛出了一个异常,而这个异常没有被程序捕获,导致这个线程异常终止进入
TERMINATED状态 。
在Java5.0及以上版本中,线程的整个六种状态 都以枚举类型的情势定义在java.lang.Thread
类中了,代码如下:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
sleep()和wait()的差别
sleep()法子 和wait()法子 都成产生 让当前运行的线程收场运行的效果 ,这是它们的共同点。
下面我们来详细说说它们的不同之处。
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException {
//other code
}
其中的参数millis代表毫秒数(千分之一秒),nanos代表纳秒数(十亿分之一秒)。这两
个法子 都可以让调用它的线程沉睡(收场运行)指定的光阴,到了这个光阴,线程就会主动
醒来,变为可运行状态 (RUNNABLE),但这并不表现它马上就会被运行,因为线程调度
机制恢复线程的运行也需要 光阴。调用sleep()法子 并不会让线程释放它所持有的同步锁;而
且在这期间它也不会阻碍其它线程的运行。上面的连个法子 都声明抛出一个
InterruptedException类型的异常,这是因为线程在sleep()期间,有可能被持有它的引用的其
它线程调用它的 interrupt()法子 而中断 。中断 一个线程会导致一个InterruptedException异常
的产生 ,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态 ,如
果你的程序捕获了这个异常,那么程序就会持续履行 catch语句块(可能还有finally语句块)
以及以后的代码。
为了更好地了解interrupt()效果 ,我们来看一下下面这个例子:
public class InterruptTest {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
try {
System.out.println("我被履行
了-在sleep()法子
前");
// 收场运行10分钟
Thread.sleep(1000 * 60 * 60 * 10);
System.out.println("我被履行
了-在sleep()法子
后");
} catch (InterruptedException e) {
System.out.println("我被履行
了-在catch语句块中");
}
System.out.println("我被履行
了-在try{}语句块后");
}
};
// 启动线程
t.start();
// 在sleep()收场
前中断
它
t.interrupt();
}
}
运行效果:
1. 我被履行了-在sleep()法子 前
2. 我被履行了-在catch语句块中
3. 我被履行了-在try{}语句块后
wait()法子 也是本地法子 ,属于Object类,有三个定义:
public final void wait() throws InterruptedException {
//do something
}
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
//do something
}
wari()和wait(long timeout,int nanos)法子 都是基于wait(long timeout)法子 实现的。同样地,
timeout代表毫秒数,nanos代表纳秒数。当调用了某个对象的wait()法子 时,当前运行的线
程就会转入等候状态 (WAITING),等候别的线程再次调用这个对象的notify()或者
notifyAll()法子 (这两个法子 也是本地法子 )唤醒它,或者到了指定的最大等候光阴,线程
主动醒来。如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会
释放它持有的所有同步资源,而不限于这个被调用了wait()法子 的对象。wait()法子 同样会被
Thread类的interrupt()法子 中断 ,并产生 一个 InterruptedException异常,效果 同sleep()法子
被中断 一样。