2012-01-18 43 views
3

在我的應用程序已經寫了使用的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方法?請提出您的意見。 (此代碼在多線程環境中運行)

回答

6

我同意懶惰初始化在這裏似乎不必要的其他評論者。初始化變量記錄最簡單的方法是在靜態初始化,這是保證僅在類加載時執行一次:

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

這有一個潛在的有初始化()執行幾次,但我不認爲你不在乎,只要每一個getLogger調用都會有一個有效的記錄器實例,即使該實例變化。

+0

正如ykaganovich引用的[double-checked locking](http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java)文章中指出的那樣,他給雙重檢查日誌記錄的例子只適用於Java 5或後來。在Java 5之前,由於JVM可以在處理器之間分配線程,所以[無法避免synchronized關鍵字](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) (和它們的本地內存緩存)。一般來說,我同意在可能的情況下,單例的靜態初始化是最好的。 – 2012-04-17 02:32:32

0

是的,您需要​​那裏。首先要避免多次撥打initialize(),其次要讓logger更改對其他線程可見。

這提出了一個問題:爲什麼你不能熱切地註冊initialize()並避免同步?

+0

你的意思是說我可以調用靜態塊初始化。 – 2012-01-18 18:40:50

+0

@sunnygupta:如果這是一個問題,那麼*是* :-)。在Logger的靜態初始化塊中調用它意味着它將在第一次加載Logger類時調用。可能在某些其他類使用它時(例如通過在「靜態」字段中引用)。 – 2012-01-18 18:42:42

+0

我不需要多次初始化,因爲我每次都返回相同的對象。類型的單例實現 – 2012-01-18 18:43:03

0

我不會推薦它,因爲你會招致同步的開銷在每次調用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; 
} 

這樣,如果第二個線程進來,而第一線仍處於初始化(),第二個線程將阻止,因爲在初始化同步的()。一旦第一個線程完成初始化,第二個線程繼續,看到初始化爲真,不會重新初始化,然後繼續。

+0

這基本上是雙重檢查鎖定,除了這個代碼在幾個地方是錯誤的。 a)「初始化」需要變化。 b)檢查不同的條件'!initialized' vs'logger == null'是危險的。 getLogger還應該檢查'!初始化' – ykaganovich 2012-01-18 19:02:38

+0

a)絕對正確。 b)也是如此,雖然我承認我試圖通過對海報代碼進行最少的更改來實現。無論如何,我更喜歡你的靜態初始化塊。 – 2012-01-18 19:34:48