java并发编程(1)并发程序的取消于关闭
一、任务的取消于关闭
1、中断Thread
1.每个线程都有一个boolean类型的中断状态。true则是中断状态中
interrupt:发出中断请求;isInterrupt:返回中断状态;interrupted:清除中断状态
2.JVM中的阻塞方法会检查线程中断状态,其响应方法为:清除中断状态,抛出InterruptedException异常,表示阻塞操作被中断结束 ;但JVM不保证阻塞方法何时检测到线程的中断状态
3.中断的理解:不会真正的中断一个正在运行的线程,而只是发出请求,具体的中断由任务自己处理
通过中断来取消线程通常是最好的方法
public class PrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted())
queue.put(p = p.nextProbablePrime());
} catch (InterruptedException consumed) {
/* Allow thread to exit */
//如果捕获到中断异常,则由线程自己退出
}
}
public void cancel() {
interrupt();
}
}
2、不可中断的阻塞的中断
如:Socket I/O操作,即使设置了中断请求,也不会中断,但是close 套接字,会使其抛出异常,达到中断效果;因此我们要重写中断方法
//自定义callable实现类
public abstract class SocketUsingTask <T> implements CancellableTask<T> {
private Socket socket; protected synchronized void setSocket(Socket s) {
socket = s;
}
//取消方法
public synchronized void cancel() {
try {
if (socket != null)
socket.close();
} catch (IOException ignored) {
}
}
//新建实例的方法
public RunnableFuture<T> newTask() {
return new FutureTask<T>(this) {
public boolean cancel(boolean mayInterruptIfRunning) {
try {
SocketUsingTask.this.cancel();
} finally {
return super.cancel(mayInterruptIfRunning);
}
}
};
}
} //自定义callable接口
interface CancellableTask <T> extends Callable<T> {
void cancel();
RunnableFuture<T> newTask();
}
//自定义 执行池
class CancellingExecutor extends ThreadPoolExecutor {
......
//通过改写newTaskFor 返回自己的Callable
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
if (callable instanceof CancellableTask)
return ((CancellableTask<T>) callable).newTask();
else
return super.newTaskFor(callable);
}
}
3、通过自定义取消计时任务
private static final ScheduledExecutorService cancelExec = newScheduledThreadPool(1);
/**
*
* @param r 任务
* @param timeout 超时时间
* @param unit TimeUnit
* @throws InterruptedException
*/
public static void timedRun(final Runnable r,long timeout, TimeUnit unit) throws InterruptedException {
class RethrowableTask implements Runnable {
//通过一个volatile变量,来存储线程是否异常
private volatile Throwable t;
public void run() {
try {
r.run();
} catch (Throwable t) {
this.t = t;
}
}
private void rethrow() {
if (t != null)
throw launderThrowable(t);
}
}
RethrowableTask task = new RethrowableTask();
final Thread taskThread = new Thread(task);
taskThread.start();
//延时timeout个unit单位后 执行线程中断
cancelExec.schedule(() -> taskThread.interrupt(), timeout, unit);
//无论如何都等待;如果线程不响应中断,那么通过join等待任务线程timeout时间后 不再等待,回到调用者线程
taskThread.join(unit.toMillis(timeout));
//如果 任务线程中有异常,则抛出
task.rethrow();
}
注意:依赖于join,任务超时join退出 和 任务正常join推出 无法进行判断
4、通过Futrue来实现取消计时任务
private static final ExecutorService taskExec = Executors.newCachedThreadPool();
public static void timedRun(Runnable r,long timeout, TimeUnit unit) throws InterruptedException {
Future<?> task = taskExec.submit(r);
try {
//通过Futrue.get(超时时间),捕获相应的异常来处理计时运行和取消任务
task.get(timeout, unit);
} catch (TimeoutException e) {
// task will be cancelled below
} catch (ExecutionException e) {
// exception thrown in task; rethrow
throw launderThrowable(e.getCause());
} finally {
// Harmless if task already completed
task.cancel(true); // interrupt if running
}
}
二、停止基于线程的服务
1.通常,服务不能直接中断,造成服务数据丢失
2.线程池服务也不能直接中断
1、日志服务
标准的生产者,消费者模式
public class LogService {
private final BlockingQueue<String> queue;
private final LoggerThread loggerThread;
private final PrintWriter writer;
private boolean isShutdown;
private int reservations;
public LogService(Writer writer) {
this.queue = new LinkedBlockingQueue<String>();
this.loggerThread = new LoggerThread();
this.writer = new PrintWriter(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(/*...*/);
}
++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 */
//捕获了中断请求,但为了将剩余日志输出,不做处理,直到计数器 == 0时,关闭
}
}
} finally {
writer.close();
}
}
}
}
2、ExecutorService中断
shutDown和shutDownNow
通常,将ExecetorService封装;如LogService,使其具有自己的生命周期方法
shutDownNow的局限性:不知道当前池中的线程状态,返回未开始的任务,但不能返回已开始未结束的任务
public class TrackingExecutor extends AbstractExecutorService {
private final ExecutorService exec;
private final Set<Runnable> tasksCancelledAtShutdown =
Collections.synchronizedSet(new HashSet<Runnable>());
public TrackingExecutor() {
exec = Executors.newSingleThreadExecutor();
}
/*public TrackingExecutor(ExecutorService exec) {
this.exec = exec;
}*/
public void shutdown() {
exec.shutdown();
}
public List<Runnable> shutdownNow() {
return exec.shutdownNow();
}
public boolean isShutdown() {
return exec.isShutdown();
}
public boolean isTerminated() {
return exec.isTerminated();
}
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return exec.awaitTermination(timeout, unit);
}
public List<Runnable> getCancelledTasks() {
if (!exec.isTerminated())
throw new IllegalStateException(/*...*/);
return new ArrayList<Runnable>(tasksCancelledAtShutdown);
}
public void execute(final Runnable runnable) {
exec.execute(new Runnable() {
public void run() {
try {
runnable.run();
} finally {
if (isShutdown()
&& Thread.currentThread().isInterrupted())
tasksCancelledAtShutdown.add(runnable);
}
}
});
}
@Test
public void test() throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
TrackingExecutor trackingExecutor = new TrackingExecutor();
trackingExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.err.println("123123");
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); //设置状态 或继续抛,在execute中处理
e.printStackTrace();
} finally {
}
}
});
List<Runnable> runnables = trackingExecutor.shutdownNow();
trackingExecutor.awaitTermination(10,TimeUnit.SECONDS);
List<Runnable> cancelledTasks = trackingExecutor.getCancelledTasks();
System.err.println(cancelledTasks.size());
}
}
三、处理非正常线程终止
1.未捕获的Exception导致的线程终止
1.手动处理未捕获的异常
2.通过Thread的API UncaughExceptionHandler,能检测出某个线程又遇见未捕获而导致异常终止
注意:默认是将异常的的堆栈信息 输出到控制台;自定义的Handler:implements Thread.UncaughExceptionHandler覆写方法
可以为每个线程设置,也可以设置一个全局的ThreadGroup
Thread.setUncaughtExceptionHandler/Thread.setDefaultUncaughtExceptionHandler
2.JVM退出、守护线程等
java并发编程(1)并发程序的取消于关闭的更多相关文章
- <<java 并发编程>>第七章:取消和关闭
Java没有提供任何机制来安全地终止线程,虽然Thread.stop和suspend等方法提供了这样的机制,但是存在严重的缺陷,应该避免使用这些方法.但是Java提供了中断Interruption机制 ...
- Java并发编程:并发容器之ConcurrentHashMap(转载)
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- Java并发编程:并发容器之ConcurrentHashMap
转载: Java并发编程:并发容器之ConcurrentHashMap JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的 ...
- Java并发编程:并发容器ConcurrentHashMap
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- 【转】Java并发编程:并发容器之ConcurrentHashMap
JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法的代价就是严重降低了 ...
- Java并发编程:并发容器之ConcurrentHashMap(转)
本文转自:http://www.cnblogs.com/dolphin0520/p/3932905.html Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载 ...
- 10、Java并发编程:并发容器之ConcurrentHashMap
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java并发编程:并发容器之CopyOnWriteArrayList
转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...
- 【Java并发编程】并发编程大合集-值得收藏
http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用 ...
随机推荐
- base64编码问题
最近遇到一个很奇怪的问题:post方式上传文件,因为文件不大,所以直接base64后作为参数扔给服务器.一开始好用,后来出问题了,上传的压缩包再下载后,能双击打开看到压缩包里面的文件,但是解压就报错, ...
- 开源的 .Net Core MVC CMS 推荐
简介 ZKEACMS Core 是基于 ZKEACMS 的 Asp.Net Core 版本. 架设环境: .Net Core 跨平台 Microsoft Sql Serverl 2008 或以上 .N ...
- Ubuntu16.04 JAVA配置!
下面是转发的文章,写得不错,不过有些地方需要注意: 1,以root身份登入,安装和配置JDK,这样JDK是全局的,其他用户也可以使用! 2,下面文章中的JDK版本应该比我们目前能够下载的要旧,我们把下 ...
- “全栈2019”Java第七十五章:内部类持有外部类对象
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- mysql扩展库应用---在线词典程序范例
1,在mysql中创建数据表words. create table words( id int primary key not null auto_increment, enword varchar( ...
- [ActionSprit 3.0] FMS服务器带宽检测
package { import flash.display.Sprite; import flash.net.NetConnection; import flash.events.NetStatus ...
- 磁盘磁盘MBR与GPT的区别
基本磁盘与动态磁盘 磁盘的使用方式可以分为两类:一类是“基本磁盘”.基本磁盘非常常见,我们平时使用的磁盘类型基本上都是“基本磁盘”.“基本磁盘”受26个英文字母的限制,也就是说磁盘的盘符只能是2 ...
- 万事不求人系列之-智能点餐算法实现-JavaScript实现智能点餐
作为一个成长中的架构师,编码能力是万不能停止的,这个算法是之前在上一家单位帮助同事们自助订餐写的,纯爱好自己码敲的,刚好这段时间重新整理代码,发现了它,分享给大家,请大家品评指教. 使用场景介绍:随着 ...
- [转](译)KVO的内部实现
转载自:http://www.cocoachina.com/applenews/devnews/2014/0107/7667.html 09年的一篇文章,比较深入地阐述了KVO的内部实现. K ...
- c语言-折半查找的函数
void search(int n,int num[],char name[N][10]) { int top,bottom,middle,location,flag; top=0; bottom=N ...