学习总结
本篇是对学习【Java编程思想 第 21 章 并发-基本的线程机制】的学习总结。
标签: Java Thread
启动线程
实现Runnablejiek
继承Thread
使用Executor
//创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
ExecutorService executor1 = Executors.newCachedThreadPool();
//创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
ExecutorService executor2 = Executors.newFixedThreadPool(5);
//创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
ExecutorService executor3 = Executors.newSingleThreadExecutor();
单个的Executor
被用来创建和管理系统中所有任务。
对ExecutorService shutdown()
方法的调用可以防止新任务被提交给这个Executor
。
当SingleThreadExecutor
被提交多个任务后,每个任务都会在下一个任务开始之前结束。
//在未来某个时间执行给定的命令。
executor.execute(new Fibonacci4("FixedThreadPool", i));
从任务中产生返回值
实现Callable
接口,提供泛型参数,表示从call()
方法中返回的值类型。
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
return "result with " + id;
}
}
然后调用ExecutorService
的submit(Callable<T> task)
方法。该方法提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
利用Future的get()方法获取任务的返回值,该方法会一直阻塞,知道结果被算出,其重载方法get(long timeout, TimeUnit unit)
表示如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
休眠
Thread.sleep();
TimeUnit.MILLISECONDS.sleep();
让步
Thread.yield();
建议具有相同优先级的线程可以运行。
优先级
//用Thread.currentThread()来获得对驱动该任务的Thread对象的引用
Thread.currentThread().setPriority(priority);
可以用setPriority()
和getPriority()
设置或获取当前线程的优先级。尽管Java有10个优先级,但它与多数操作系统都不能很好地映射。唯一可移植的方法是当调整优先级时,只是用MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY三种级别。
后台线程
所谓后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要后台线程存活,程序就不会终止。
// Must call before start()
daemon.setDaemon(true);
必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。
如果一个线程是后台线程,那么它创建的任何线程都将被自动设置成后台线程。
多种形式创建线程
extends Thread + 在构造函数中启动线程
public class SimpleThread extends Thread {
public SimpleThread() {
start();
}
}
implements Runnanle + 持有Thread对象 + 在构造函数中启动线程
public class SelfManaged implements Runnable {
private Thread t = new Thread(this);
public SelfManaged() { t.start(); }
}
Inner Class extends Thread
Anonyous inner class + 持有Thread对象 + 在构造函数中启动线程
public InnerRunnable2(String name) {
t = new Thread(new Runnable() {
public void run() {
try {
while(true) {
print(this);
TimeUnit.MILLISECONDS.sleep(10);
}
} catch(InterruptedException e) {
print("sleep() interrupted");
}
}
}, name);
t.start();
}
Inner Class implements Runable + 持有Thread对象 + 在构造函数中启动线程
private class Inner implements Runnable {
Thread t;
Inner(String name) {
t = new Thread(this, name);
t.start();
}
public void run() {
try {
while(true) {
print(this);
if(--countDown == 0) return;
TimeUnit.MILLISECONDS.sleep(10);
}
} catch(InterruptedException e) {
print("sleep() interrupted");
}
}
}
加入线程
Thread.join()
如果某个线程在另一个线程上调用另一个线程t
的t.join()
,此线程将被挂起,知道目标线程t
结束才恢复。
无法捕获的异常
由于线程的特殊性,使得你不能捕获从线程中逃逸的异常。
看个例子:
import java.util.concurrent.*;
class ExceptionThread implements Runnable {
public void run() {
throw new RuntimeException();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
public class NaiveExceptionHandling {
public static void main(String[] args) {
try {
ExecutorService exec =
Executors.newCachedThreadPool();
//将发生异常
exec.execute(new ExceptionThread());
// 由于异常没有被捕获,这句话得以执行
exec.shutdown();
} catch(RuntimeException ue) {
// 这句话不会被执行
System.out.println("Exception has been handled!");
}
}
}
解决之道:
Thread.setDefaultUncaughtExceptionHandler
是Java SE 5
的新接口,它允许你在每个Thread
对象上都附着一个异常处理器,它会在线程因为未被捕获的异常而临近死亡的时候被调用。
class MyUncaughtExceptionHandler implements
Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
当然你可以用ThreadFactory
为每个生成的Thread附着上这样一个定制的Thread.setDefaultUncaughtExceptionHandler
。
更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未被捕获异常处理器。
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler());