-- Start
线程与进程的区别
线程之间可以共享内存, 而每个进程有自己独立的内存空间.
定义线程
继承 Thread
定义线程的一种方式是继承 Thread, 然后覆盖 Thread 类的 run 方法.
public class Test {
public static void main(String[] args) throws Exception {
MyThread myThread = new MyThread();
myThread.start(); // 启动线程
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("to do my work.");
}
}
实现 Runnable
定义线程的另一种方式是实现 Runnable, 然后实现 Runnable 的 run 方法.
public class Test {
public static void main(String[] args) throws Exception {
MyRun myRun = new MyRun();
Thread myThread = new Thread(myRun);
myThread.start(); // 启动线程
}
}
class MyRun implements Runnable {
public void run() {
System.out.println("to do my work.");
}
}
注意 start 用来启动线程, 而不是 run 方法. 通常我们推荐使用第二种方式来创建线程, 如果我们不打算修改或增强某个类, 我们不应该随便继承一个类.
线程优先级(priority)
每个线程都有一个优先级, 线程调度器在调度线程时会优先选择那些优先级较高的线程. 我们可以通过 getPriority 方法来查看线程的优先级, 我们可以通过 setPriority 方法来设置优先级, 我们可以设置从 Thread.MIN_PRIORITY 到 Thread.MAX_PRIORITY 的任何值(从1-10), 通常我们选择 Thread.NORM_PRIORITY (5).
Thread 的 yield 方法可以让线程暂时放弃运行, 此时线程调度器可以选择其他线程来运行.
守护线程(daemon)
守护线程用来为其他线程提供服务, 所以在一般情况下, 它会常驻在内存中而不会结束, 当只剩下守护进程时, 守护进程就终止了, 因为其他线程都结束了, 它还提供服务给谁呢? 我们可以通过 isDaemon 方法来判断一个线程是否是守护线程, 我们也可以通过 setDaemon 方法来设置一个普通线程为一个守护线程, 不过它必须在线程启动之前调用.
线程状态
通过 Thread 的 getState 方法我们可以得到线程的状态, 枚举类 Thread.State 封装了线程的状态, 线程可以有如下 6 种状态.
- NEW: 在调用 start 方法之前, 线程处于此状态.
- RUNNABLE: 线程正在运行或等待线程调度器调度.
- BLOCKED: 线程等待某个对象的锁.
- WAITING: 线程等待其他线程唤醒.
- TIMED_WAITING: 线程等待其他线程唤醒或超时
- TERMINATED: 线程终止.
中断状态 (interrupted status)
如果我们想停止当前正在运行的线程该怎么办呢? Thread 类有一个 stop 方法用来停止线程, 可惜它已经不推荐使用了, 原因是它强制终止线程, 极有可能会破坏对象.
虽然现在我们无法强制终止一个线程, 但是我们却可以通过 interrupt 方法来请求终止一个线程. 它会设置线程的中断状态标志位, 每个线程都应该时不时的判断该标志位以决定是否终止线程.
如果一个线程由于调用了 wait, join 和 sleep 等方法而被阻塞了, 那么它就没有机会去检查中断状态了, 如果此时我们调用 interrupt 方法来试图终止该线程, 那么 wait, join 和 sleep 等方法会抛出 InterruptedException 异常从而退出阻塞状态.
每个线程可以自己决定如何应对中断请求, 我们可以终止线程, 也可以忽略该中断请求.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws Exception {
MyRun myRun = new MyRun();
Thread myThread = new Thread(myRun);
myThread.start(); // 启动线程
// 假设该线程从一个很大的文件读取数据
// 运行一段时间后, 由于某种原因, 我们想停止该线程
// 设置该线程的中断状态为 true
myThread.interrupt();
}
}
class MyRun implements Runnable {
public void run() {
try {
Scanner s = new Scanner(new File("test.txt"));
// 通过 isInterrupted 方法检查中断状态, 如果中断状态为 true, 则退出循环
while (Thread.currentThread().isInterrupted() && s.hasNextLine()) {
String line = s.nextLine();
System.out.println(line);
Thread.sleep(3000);
}
} catch (InterruptedException e) {
// 在 sleep 方法运行前或运行中调用 interrupt方法将会抛出此异常来跳出阻塞状态并清除中断状态
// 我们应该再次设置中断状态
Thread.currentThread().interrupt();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
线程组(ThreadGroup)
正如文件夹用来管理一组相关的文件一样, 线程组用来管理一组相关的线程, 我们可以通过 ThreadGroup 类的构造方法来创建一个线程组,
在创建线程时也可以指定一个线程组. 一旦创建了线程组, 我们就可以利用它提供的方法来对一组线程进行操作, 省去了我们逐个操作的麻烦.
异常处理器
线程的 run 方法不能抛出被检查异常, 但是我们可以给线程指定异常处理器.
设置或获得默认异常处理器
- getDefaultUncaughtExceptionHandler
- setDefaultUncaughtExceptionHandler
设置或获得某个线程的异常处理器
- getUncaughtExceptionHandler
- setUncaughtExceptionHandler
---更多参见:Java 精萃
-- 声 明:转载请注明出处-- Last Updated on 2012-06-20
-- Written by ShangBo on 2012-06-20
-- End