任務取消
Java沒有提供任何機制來安全地終止線程。但它提供了中斷(Interruption),這是一種協作機制,能夠使一個線程終止另一個線程的當前工作。
設置“已請求取消(Cancellation Requested)”標誌。
示例代碼:
public class PrimeGenerator implements Runnable {
private volatile boolean cancelled;
private final List<BigInteger> primes = new ArrayList<>();
@Override
public void run() {
BigInteger p = BigInteger.ONE;
while (!cancelled) {
p = p.nextProbablePrime();
synchronized (this) {
primes.add(p);
}
}
}
public void cancel() {
cancelled = true;
}
public synchronized List<BigInteger> get() {
return new ArrayList<>(primes);
}
public static void main(String[] args) throws InterruptedException {
PrimeGenerator pg = new PrimeGenerator();
new Thread(pg).start();
try {
SECONDS.sleep(1);
} finally {
pg.cancel();
}
List res = pg.get();
System.out.println("Size: " + res.size() + " " + res);
}
}
通常,中斷是實現取消的最合理方式。Thread中的中斷方法如下:
public class Thread {
/**
* 中斷目標線程,即自己
**/
public void interrupt() {...}
/**
* 返回目標線程的中斷狀態
**/
public boolean isInterrupted() {...}
/**
* 清除當前線程的中斷狀態,並返回它之前的值
**/
public static boolean interrupted() {...}
}
示例代碼:
public class NewPrimeGenerator extends Thread {
private final BlockingQueue<BigInteger> queue = new ArrayBlockingQueue<BigInteger>(1000000);
@Override
public void run() {
try {
BigInteger p = BigInteger.ONE;
queue.put(p);
while (!Thread.currentThread().isInterrupted()) {
p = p.nextProbablePrime();
queue.put(p);
}
} catch (InterruptedException e) {
System.out.println("Interrupted exit....");
} catch (Exception a) {
System.out.println("Other Exception" + a);
}
}
public void cancel() {
interrupt();
}
public List<BigInteger> get() {
return new ArrayList<>(queue);
}
public static void main(String[] args) throws InterruptedException {
NewPrimeGenerator npg = new NewPrimeGenerator();
npg.start();
try {
SECONDS.sleep(1);
} finally {
npg.cancel();
}
List res = npg.get();
System.out.println("Size: " + res.size() + " " + res);
}
}
通過Future來實現取消
Future擁有一個cancel方法,該方法帶有一個boolean類型的參數mayInterruptIfRunning,表示取消操作是否可以成功。
示例代碼:
public static void timedRun(Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
Future<?> task = taskExec.submit(r);
try {
task.get(timeout, unit);
} catch (TimeoutException e) {
//任務將被取消
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
} finally {
task.cancel(true);//如果任務正在運行,則將被中斷,否則直接取消
}
}
封裝非標準的取消操作
如果一個線程由於執行同步的Socket I/O或者等待獲得內置鎖而阻塞,那麼中斷請求只能設置線程的中斷狀態,除此之外沒有其他任何作用。
示例代碼:
public class ReaderThread extends Thread {
private final Socket socket;
private final InputStream in;
private final int BUFSIZ = 1024;
private ReaderThread(Socket socket) throws IOException {
this.socket = socket;
this.in = socket.getInputStream();
}
@Override
public void interrupt() {
try {
socket.close();
} catch (IOException e) {
} finally {
super.interrupt();
}
}
@Override
public void run() {
try {
byte[] buf = new byte[BUFSIZ];
while (true) {
int count = in.read(buf);
if (count < 0)
break;
else if (count > 0)
processBuffer(buf, count);
}
} catch (IOException e) {
}
}
private void processBuffer(byte[] buf, int count) {
//some work done
}
}
任務關閉
通過解決競態條件問題提供可靠關閉操作。
代碼:
public class LogService {
private final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(Integer.MAX_VALUE);
private final LoggerThread loggerThread = new LoggerThread();
private final PrintWriter writer;
private boolean isShutdown = false;
private int reservations = 0;
public LogService(PrintWriter writer) { this.writer = writer; }
public void start() { loggerThread.start(); }
public void stop() {
synchronized (this) { isShutdown = true; }
loggerThread.interrupt();
}
public void log(String msg) throws InterruptedException {
synchronized (this) {
if (isShutdown)
throw new IllegalStateException("Service shutdown!");
++reservations;
}
queue.put(msg);
}
private class LoggerThread extends Thread {
public void run() {
try {
while (true) {
try {
synchronized (LogService.this) {
if (isShutdown && reservations == 0)
break;
}
String msg = queue.take();
synchronized (LogService.this) {
--reservations;
}
writer.println(msg);
} catch (InterruptedException e) {
// retry?
}
}
} finally {
writer.close();
}
}
}
}
使用shutdown以及shutdownNow方法,關閉ExecutorService
示例代碼:
public class NewLogService {
private final ExecutorService exec = Executors.newSingleThreadExecutor();
private PrintWriter writer;
public NewLogService(PrintWriter writer) {
this.writer = writer;
}
public void start() {
}
public void stop() throws InterruptedException {
try {
exec.shutdown();
exec.awaitTermination(10, TimeUnit.SECONDS);
} finally {
writer.close();
}
}
public void log(String msg) {
try {
exec.execute(new WriteTask(msg));
} catch (RejectedExecutionException e) {
}
}
}
“毒丸”對象
生產者再提交了“毒丸”對象後,將不會提交其他工作,消費者接收到“毒丸”對象之前完成隊列中的所有工作
示例代碼:
public class IndexingService {
private static final File POISON = new File("");
private final IndexerThread consumer = new IndexerThread();
private final CrawlerThread producer = new CrawlerThread();
private final BlockingQueue<File> queue = new ArrayBlockingQueue<File>(Integer.MAX_VALUE);
private final FileFilter fileFilter;
private final File root;
public IndexingService(FileFilter fileFilter, File root) {
this.fileFilter = fileFilter;
this.root = root;
}
class CrawlerThread extends Thread {
@Override
public void run() {
try {
crawl(root);
} catch (Exception e) {
} finally {
while (true) {
try {
queue.put(POISON);
break;
} catch (InterruptedException e) {
//retry
}
}
}
}
private void crawl(File root) { /* dig */ }
}
class IndexerThread extends Thread {
@Override
public void run() {
try {
while (true) {
File file = queue.take();
if (POISON == file) {
break;
} else {
indexFile(file);
}
}
} catch (Exception e) {
} finally {
while (true) {
try {
queue.put(POISON);
break;
} catch (InterruptedException e) {
//retry
}
}
}
}
private void indexFile(File file) { /* index */ }
}
}
未捕獲異常的處理
在Thread API中提供了UncaughtExceptionHandler,它能檢測出某個線程由於未捕獲的異常而終結的情況。只有通過execute提交的任務,才能將他拋出的異常交給未捕獲異常處理器,而通過submit提交的任務,無論是拋出的未檢查異常還是已檢查異常,都將被認爲是任務返回狀態的一部分。
示例代碼:
public class UEHTest {
private static class MyUEH implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.SEVERE, "Thread terminated with exception: " + t.getName(), e);
}
}
private static class MyThread extends Thread {
@Override
public void run() {
Integer.parseInt("aaa");
}
}
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.setUncaughtExceptionHandler(new MyUEH());
myThread.start();
//Not working
/*ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(myThread);*/
}
}
示例代碼:
public class UEHExecutorService {
private static class MyUEH implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.SEVERE, "Thread terminated with exception: " + t.getName(), e);
}
}
private static class MyThreadFactory implements ThreadFactory {
private final Thread.UncaughtExceptionHandler handler = new MyUEH();
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setUncaughtExceptionHandler(handler);
return thread;
}
}
public static void main(String[] args) {
ThreadFactory factory = new MyThreadFactory();
ExecutorService exec = new ThreadPoolExecutor(10, 100, 180, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), factory);
Thread thread = factory.newThread(new Runnable() {
@Override
public void run() {
Integer.parseInt("abc");
}
});
exec.execute(thread);
}
}
參考:《Java併發編程實戰》、爲線程池中的每個線程設置UncaughtExceptionHandler、線程池執行UncaughtExceptionHandler失效問題分析