任务取消
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失效问题分析