【问题标题】:Using Synchronization while Logging在记录时使用同步
【发布时间】:2012-01-18 18:35:33
【问题描述】:

在我的应用程序中,我使用 Java.Util.Logging 编写了自己的日志记录实用程序

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.SimpleFormatter;



public class Logger {

    public final static String PROPERTIES_FILE = "Logger.properties";
    private static java.util.logging.Logger logger = null;

    private static void initialize() {
        try {
            logger = java.util.logging.Logger.getLogger(Logger.class.getName());
            FileHandler fh = new FileHandler("D:\\MyLogFile.log", true);
            logger.addHandler(fh);
            logger.setLevel(Level.ALL);
            SimpleFormatter formatter = new SimpleFormatter();
            fh.setFormatter(formatter);
            logger.log(Level.INFO, "Test Message Logged");

        }
        catch (IOException e) {
          System.out.println("Warning: Unable to read properties file " +
                             PROPERTIES_FILE );
        }   
      }

    public static synchronized java.util.logging.Logger getLogger(String name)
    {
        if(logger==null)
        {
        Logger.initialize();
        }
        logger.getLogger(name);
        return logger;
    }


}

我需要对 getLogger 方法使用同步吗?请给你的cmets。 (此代码运行在多线程环境中)

【问题讨论】:

    标签: java multithreading logging


    【解决方案1】:

    我同意其他评论者的观点,即这里似乎不需要延迟初始化。初始化 logger 变量的最简单方法是在静态初始化程序中,它保证只在类加载时执行一次:

    public class Logger {
    
        public final static String PROPERTIES_FILE = "Logger.properties";
        private static java.util.logging.Logger logger = null;
    
        private static void initialize() {
            try {
                logger = java.util.logging.Logger.getLogger(Logger.class.getName());
                FileHandler fh = new FileHandler("D:\\MyLogFile.log", true);
                logger.addHandler(fh);
                logger.setLevel(Level.ALL);
                SimpleFormatter formatter = new SimpleFormatter();
                fh.setFormatter(formatter);
                logger.log(Level.INFO, "Test Message Logged");
    
            }
            catch (IOException e) {
              System.out.println("Warning: Unable to read properties file " +
                                 PROPERTIES_FILE );
            }   
        }
    
        static {
            initialize();
        }
    
        public static java.util.logging.Logger getLogger(String name)
        {
            logger.getLogger(name);
            return logger;
        }
    
    
    }
    

    但是,您可以避免与double-checked locking 进行大多数同步。

    public class Logger {
    
        // note: volatile is required
        private volatile static java.util.logging.Logger logger = null;
    
        //... 
    
        public static java.util.logging.Logger getLogger(String name)
        {
            if(logger==null)
            {
               synchronized(Logger.class) 
               {
                  if(logger == null)
                    Logger.initialize();
                  }
               }
            }
            logger.getLogger(name);
            return logger;
        }
    }
    

    事实上,在你的情况下,我认为你可以完全避免同步,如果你重写你的初始化函数,以便它在将记录器分配给(易失性)类变量之前将它完全配置在一个局部变量中:

    private volatile static java.util.logging.Logger logger = null;
    private static void initialize() {
        try {
            Logger logger = java.util.logging.Logger.getLogger(Logger.class.getName());
            FileHandler fh = new FileHandler("D:\\MyLogFile.log", true);
            logger.addHandler(fh);
            logger.setLevel(Level.ALL);
            SimpleFormatter formatter = new SimpleFormatter();
            fh.setFormatter(formatter);
            logger.log(Level.INFO, "Test Message Logged");
    
            Logger.logger = logger;
        }
        catch (IOException e) {
          System.out.println("Warning: Unable to read properties file " +
                             PROPERTIES_FILE );
        }   
    
        public static java.util.logging.Logger getLogger(String name)
        {
            if(logger==null)
            {
            Logger.initialize();
            }
            logger.getLogger(name);
            return logger;
        }
    }
    

    这有可能让 initialize() 执行多次,但我认为你不在乎,只要每次 getLogger 调用都会有一个有效的记录器实例,即使该实例不同。

    【讨论】:

    • 正如 ykaganovich 引用的 double-checked locking 文章中所指出的,他给出的双重检查日志的示例仅适用于 Java 5 或更高版本。在 Java 5 之前,有 no way to avoid the synchronized keyword,因为 JVM 可以跨处理器(及其本地内存缓存)分配线程。一般来说,我同意单例的静态初始化是最好的。
    【解决方案2】:

    是的,你需要synchronized。一是避免多次调用initialize(),二是让logger更改对其他线程可见。

    这引出了一个问题:您为什么不能急切地initialize() logger 并完全避免同步?

    【讨论】:

    • 你的意思是说我可以从静态块调用初始化。
    • @sunnygupta:如果这是个问题,那么是的 :-)。在Logger 的静态初始化块中调用它意味着一旦第一次加载Logger 类就会调用它。可能在其他类使用它时(例如,通过在 static 字段中引用)。
    • 我不需要多次调用初始化,因为我每次都返回同一个对象。一种单例实现
    • @sunnygupta:使用static 初始化块保证(抛开不同的类加载器)
    【解决方案3】:

    我不推荐它,因为当您只需要它一次进行初始化时,每次调用getLogger() 都会产生同步开销。我会这样做:

    private static boolean initialized = false;
    
    private static synchronized void initialize() {
        try {
           if(!initialized)
           {            
            // Do initialization
            initialized = true;
           }
    
        }
        catch (IOException e) {
          System.out.println("Warning: Unable to read properties file " +
                             PROPERTIES_FILE );
        }   
      }
    
    public static java.util.logging.Logger getLogger(String name)
    {
        if(logger==null)
        {
        Logger.initialize();
        }
        logger.getLogger(name);
        return logger;
    }
    

    这样,如果第二个线程进入,而第一个线程仍在 initialize() 中,第二个线程将因为在 initialize() 上同步而阻塞。一旦第一个线程完成初始化,第二个线程继续执行,看到 initialized 为真,不重做初始化,然后继续。

    【讨论】:

    • 这本质上是双重检查锁定,除了这段代码在几个地方是错误的。 a) initialized 需要是易变的。 b) 检查!initializedlogger==null 的不同条件是危险的。 getLogger 还应该检查!initialized
    • a) 绝对正确。 b)也是正确的,尽管我承认我试图以最少的对海报代码的更改来做到这一点。无论如何,我更喜欢你的静态初始化块。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-24
    • 2015-07-14
    相关资源
    最近更新 更多