• Java并发编程(六) 一个日志服务的例子


      日志服务需要提供的功能有:

    可以从外部安全地开启和关闭日志服务;

    可以供多个线程安全地记录日志消息;

    在日志服务关闭后,可以把剩余未记录的消息写入日志文件;

    public class LogService
    {
       private final BlockingQueue<String> msgQueue;         //阻塞的消息队列保存日志消息
       private final PrintWrite writer;                      //写消息到日志文件
       private final LoggerThread logThread;                 //写日志的线程
       private boolean isShutdown;                           //表示日志服务是否已经关闭
    
       public LogService(String file) throws FileNotFoundException
       {
          logThread = new LogThread();
    writer = new PrintWrite(file); }
    public void start() { logThread.start(); //启动日志线程 Runtime.getRuntime().addShutdownHook(new Thread() { //添加关闭钩子,确保在没有调用stop方法的情况下,日志文件最终仍然会关闭 stop(); }); } public void stop() { synchronized(this) //需要先加锁,再修改isShutdown的值 { if(!isShutdown) { isShutdown = true; logThread.interrupt(); //中断日志线程 } } } public void log(String message) { synchronized(this) //需要先加锁,再访问isShutdown的值 { if(!isShutdown) //若日志服务没有关闭,则将消息加入消息队列,这里是典型的先验条件,声明isShutdown为volatile并不能解决同步的问题 msgQueue.put(message); else throw new IllegalStateException("Log Service is shutdown"); //若日志服务已经关闭,则抛出IllegalStateException } } private class LoggerThread extends Thread { public void run() { try { while(true) { try { synchronized(LogService.this) { if(isShutdown && msgQueue.size() == 0) //如果服务已经关闭并且消息队列中已经没有剩余的消息,则关闭日志线程 break; writer.write(msgQueue.take()); } } catch(InterruptedException ex){} //忽略中断消息 } } finally { writer.close(); //关闭日志文件 } } } }

     在上面的例子中,有以下几个地方值得注意:

    日志服务不应该在收到关闭消息时立即停止,而应该将消息队列中剩余的消息写入到日志文件之后再关闭。如果决定丢弃这些消息,那么应该先清空消息队列,否则调用log方法的线程会一直阻塞;

    上例中使用isShutdown来标识服务是否已经关闭,调用log方法的线程首先检测isShutdown的值,这样多个线程就需要对isShutdown互斥访问,而不能简单使用volatile修饰isShutdown;

    在日志线程中,检测到中断消息后,直接忽略了,最后在finally中也没有再恢复中断状态,这是因为我们知道线程的所有者日志服务已经停止了,不再需要恢复中断;

    上例中使用了关闭钩子,在start方法中添加了关闭钩子线程,可以确保即使调用者没有调用stop方法停止日志服务,日志服务最终在JVM停止之前也会关闭;

     下面简单介绍一下关闭钩子:

    关闭钩子是通过Runtime.addShutdown方法注册的但并不立刻启动任务的线程,JVM在关闭过程中,首先会启动执行已经注册的关闭钩子线程。关闭钩子通常用于实现服务或者应用程序的清理工作,并且不宜在其中执行耗时的任务,会延迟JVM关闭的时间。

    参考资料 《Java并发编程实战》

  • 相关阅读:
    nginx负载均衡
    docker 使用之管理工具shipyard(五)
    docker使用之私有仓库构建(四)
    docker使用之dockerfile手动构建镜像(三)
    docker 使用之网络和存储(二)
    docker部署和使用(一)
    Java微信公众号开发
    Java实现验证码的制作
    SSH框架-Struts2基础-Action
    Java算法
  • 原文地址:https://www.cnblogs.com/jqctop1/p/4952422.html
Copyright © 2020-2023  润新知