Использование синхронизации при ведении журнала - PullRequest
3 голосов
/ 18 января 2012

В своем приложении я написал свою собственную утилиту ведения журнала, используя 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? Пожалуйста, дайте ваши комментарии. (Этот код выполняется в многопоточной среде)

Ответы [ 3 ]

6 голосов
/ 18 января 2012

Я согласен с другими комментаторами, что ленивая инициализация здесь не кажется необходимой.Самый простой способ инициализировать переменную логгера - статический инициализатор, который гарантированно будет выполняться только один раз во время загрузки класса:

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;
    }


}

Однако, вы можете избежать большей части синхронизации с двойной проверкой блокировки.

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;
    }
}

На самом деле, в вашем случае, я думаю, вы можете полностью избежать синхронизации, если переписать свою функцию инициализации, чтобы она полностью сконфигурировала регистратор в локальной переменной перед назначением ее для (переменная класса volatile):

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 будет иметь действительный экземпляр logger, дажеесли этот экземпляр меняется.

0 голосов
/ 18 января 2012

Я бы не советовал, потому что вы будете нести издержки синхронизации при каждом вызове на 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 (). Как только первый поток завершает инициализацию, второй поток продолжает, видит, что инициализировано верно, не повторяет инициализацию и движется дальше.

0 голосов
/ 18 января 2012

Да, вам нужно synchronized там. Во-первых, чтобы не вызывать initialize() несколько раз, а во-вторых, logger сделать изменения видимыми для других потоков.

Возникает вопрос: почему вы не можете initialize() протоколировать и вообще избегать синхронизации?

...