JAVA线程执行中断方式和ElasticSearch未捕获异常的处理方式

JAVA线程执行中断方式

Java中只能通过协作的方式取消

  • 第一种是通过标志位实现,假设有个计算所有素数的任务,每次计算前检查下是否取消的标志位,如果为true则退出计算。调用方想要取消任务的话,则将标志位设为true。但这种方法无法再计算的过程中取消任务,像是一些阻塞调用无法被取消

  • 第二种是中断,用于通过协作机制停止线程继续执行任务,原理是向进程发送中断请求将标记线程为Interrupted,线程会在下一个合适的时刻停止运行,阻塞的库方法例如Thread.sleep Object.wait 都会响应中断,抛出InterruptedException意味着阻塞操作因为中断结束,但不能保证响应速度。

    通常任务不会在自己拥有的线程执行,而是在某个服务的线程池中执行,因此任务应该保存阻塞状态,使得线程能处理中断。这就是库函数抛出InterruptedException的原因。同时任务也不能对线程处理中断的策略做任何假设。

    有两种处理中断的方法,千万不要catch掉异常什么也不做,除非你拥有该线程

    • 向外抛出InterruptedException
    • 调用 Thread.currentThread().interrupt();恢复中断状态

    例如在向线程池提交runnable执行时,runnable调用了阻塞库函数则需要写处理中断的逻辑,逻辑中要使用上面处理中断的两种方法之一,因为线程池会处理线程遇到阻塞,将线程从可用线程列表删除,统计审计信息等操作

    final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
            w.unlock(); // allow interrupts
            boolean completedAbruptly = true;
            try {
                while (task != null || (task = getTask()) != null) {
                    w.lock();
                    try {
                        beforeExecute(wt, task);
                        Throwable thrown = null;
                        try {
                            task.run(); // 执行runnable
                        } catch (RuntimeException x) {
                            thrown = x; throw x;
                        } catch (Error x) {
                            thrown = x; throw x;
                        } catch (Throwable x) {
                            thrown = x; throw new Error(x);
                        } finally {
                            afterExecute(task, thrown);
                        }
                    } finally {
                        task = null;
                        w.completedTasks++;
                        w.unlock();
                    }
                }
                completedAbruptly = false;
            } finally {
                processWorkerExit(w, completedAbruptly); // 处理InteruptException
            }
        }
    private void processWorkerExit(Worker w, boolean completedAbruptly) {
            if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
                decrementWorkerCount();
    
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                completedTaskCount += w.completedTasks;
                workers.remove(w);
            } finally {
                mainLock.unlock();
            }
    
            tryTerminate();
    
            int c = ctl.get();
            if (runStateLessThan(c, STOP)) {
                if (!completedAbruptly) {
                    int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                    if (min == 0 && ! workQueue.isEmpty())
                        min = 1;
                    if (workerCountOf(c) >= min)
                        return; // replacement not needed
                }
                addWorker(null, false);
            }
        }
    

还有一种方法是使用future来中断任务执行,使用submit.cancel(true),内部也是利用了Thread.interrupt

ExecutorService executor = Executors.newFixedThreadPool(1);
        final Future<?> submit = executor.submit(() -> {
        });
        try {
            final Object o = submit.get(10, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            
        }finally {
            submit.cancel(true); // 正常得到结果后再cancel也无影响
        }

未捕获异常的处理方式

线程因为异常或者error退出后,会向System.error输出堆栈,但线上程序一般通过日志来排查问题而不是控制台输出。Java支持配置Thread.UncaughtExceptionHandler处理这种未被捕获的Throwable,程序最好自己实现该接口,至少将异常堆栈输出到日志中,下面是ES实现的Thread.UncaughtExceptionHandler(去掉了一些细节),ES将异常信息输出到了日志文件。

class ElasticsearchUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final Logger logger = LogManager.getLogger(ElasticsearchUncaughtExceptionHandler.class);

    @Override
    public void uncaughtException(Thread thread, Throwable t) {
        if (isFatalUncaught(t)) {
            onFatalUncaught(thread.getName(), t);
        } else {
            onNonFatalUncaught(thread.getName(), t);
        }
    }

    static boolean isFatalUncaught(Throwable e) {
        return e instanceof Error;
    }

    void onFatalUncaught(final String threadName, final Throwable t) {
        final String message = "fatal error in thread [" + threadName + "], exiting";
        logger.error(message, t);
        Terminal.DEFAULT.errorPrintln(message);
        t.printStackTrace(Terminal.DEFAULT.getErrorWriter());
        // Without a final flush, the stacktrace may not be shown before ES exits
        Terminal.DEFAULT.flush();
    }

    void onNonFatalUncaught(final String threadName, final Throwable t) {
        final String message = "uncaught exception in thread [" + threadName + "]";
        logger.error(message, t);
        Terminal.DEFAULT.errorPrintln(message);
        t.printStackTrace(Terminal.DEFAULT.getErrorWriter());
        // Without a final flush, the stacktrace may not be shown if ES goes on to exit
        Terminal.DEFAULT.flush();
    }

}

你可能感兴趣的:(java,elasticsearch,开发语言)