2012-05-13 8 views
0

2つの異なるテキストファイルに書き込む必要があるスレッドがいくつかあります。これまでのところ、私はこのコードを持っている:静的クラスでのファイル出力の同期

public class Logger { 

    public static void printToGameLog(String value){ 
     Writer writer = null; 
     try { 
      writer = new BufferedWriter(new OutputStreamWriter(
       new FileOutputStream("GameLog.txt", true), "utf-8")); 
      synchronized(writer){ 
       writer.write(outputString + "\r\n"); 
      } 
     } catch (IOException ex){ 
      System.out.println("cannot create log file"); 
     } 
    } 


    public static void printToServerLog(String value){ 
     Writer writer = null; 
     try { 
      writer = new BufferedWriter(new OutputStreamWriter(
       new FileOutputStream("serverLog.txt", true), "utf-8")); 
      synchronized(writer){ 
       writer.write(outputString + "\r\n"); 
      } 
     } catch (IOException ex){ 
      System.out.println("cannot create log file"); 
     } 
    } 
} 

が、これは1個以下のスレッドが同時に同じファイルに書き込んでいる確保の許容可能な方法は何ですか?

スレッドがこれらのメソッドのいずれかを呼び出してsyncブロックに入ると、別のスレッドが来て同じメソッドを実行しようとするとどうなりますか?ローカル変数writerを使用しようとすると、他のスレッドによってロックされた同じオブジェクトが取得され、ブロックされますか?私はそれが単なる独自の別の変数を作成すると思っていたでしょう。これは、代わりに静的なクラス変数を作者にしなければならないということでしょうか?あなたのコード内でnullポインタ例外だ

+0

2つの異なるファイルに書き込む場合は、同期する必要はありません。 – user845279

+1

'' \ r \ n "' - '' \ n "'のみを使用して、ランタイムにプラットフォームの正しい行末を書き込ませます。 –

+0

GameLogとServerLogの両方に出力を調整する目的はありますか、または単に2つのスレッドが1つの出力行内でインターリーブするのを防ぐためですか? –

答えて

1

別々のログファイルがあるので、なぜクラスレベルの同期が必要かわかりません。不必要なボトルネックのようです。私は、別々の方法で別々のファイルにヒットするので、別々の方法ごとに同期を提供しています。

public class Logger 
{ 
    private static final Object GAME_LOG_LOCK = new Object(); 
    private static final Object SERVER_LOG_LOCK = new Object(); 

    public static void printToGameLog(String value){ 
     synchronized (GAME_LOG_LOCK) { 
      Writer writer = null; 
      try { 
       writer = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream("GameLog.txt", true), "utf-8")); 
       writer.write(outputString + "\r\n"); 
      } catch (IOException ex){ 
       System.out.println("cannot create log file"); 
      } 
     } 
    } 

    public static void printToServerLog(String value){ 
     synchronized (SERVER_LOG_LOCK) { 
      Writer writer = null; 
      try { 
       writer = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream("serverLog.txt", true), "utf-8")); 
       writer.write(outputString + "\r\n"); 
      } catch (IOException ex){ 
        System.out.println("cannot create log file"); 
      } 
     } 
    } 
} 
+0

@RogerJarvisあなたはまたそれぞれのケースでファイルを閉じる必要があります。 – EJP

0

、静的メソッドに

synchronized(Logger.class){ 

または別の代替を同期ブロックを使用して、この方法を試してみてください。この

public static synchronized void printToGameLog(String value){ 
などの同期全体の方法を、設定することです

public static synchronized void printToServerLog(String value){ 

私はあなたが同期を必要とすることを確信していませんここでは、複数のスレッドから読み書きされている状態がある場合にのみ、同期が必要です。

+0

私は愚かです。もちろん、それはNPEを与えるつもりです。私は初期化された後、ライターと同期するために私の質問に言い換えるつもりだと思う。しかし、あなたの提案では、これは、あるスレッドがgameLogに書き込んでいる間に別のスレッドがserverLogへの書き込みをブロックできるということを意味していませんか? –

+0

はい、私はあなたがしたいことですが、私はあなたの奇妙なことを間違って読んだことに気付きました – ilcavero

0

これは別の問題です。ログファイルを書き込むために単一のスレッドを使用し、このスレッドのみがファイルにアクセスします。ログを取らなければならないスレッドは、BlockingQueueに対して書き込みます。

public class ThreadedLog { 

    //This is some code to test the logger 
    public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException { 

     ThreadedLog log = new ThreadedLog("/tmp/test.txt"); 
     // Start 100 thread that write against the log 
     for (int i = 0; i < 100; i++) { 
      new Thread(new TestLogger(log)).start(); 
     } 
    } 

    private static class TestLogger implements Runnable { 

     private ThreadedLog log; 

     public TestLogger(ThreadedLog log) { 
      this.log = log; 
     } 

     @Override 
     public void run() { 
      for (int i = 0; i < 5000; i++) { 
       try { 
        log.log("This is entry " + i + " from thread " + Thread.currentThread().getId()); 
       } catch (InterruptedException ex) { 
       } 
      } 
      System.out.println(Thread.currentThread().getId() + " is done"); 
     } 
    } 
    //________________________________________________________________________________________________ 
    /* 
    * This is the code for the actual logger 
    * 
    */ 
    private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(10000); 
    private String fileName; 
    private Thread thread; 
    private Writer writer; 

    public ThreadedLog(String fileName) throws UnsupportedEncodingException, FileNotFoundException { 
     this.fileName = fileName; 
     thread = new Thread(new LoggingThread()); 
     writer = new BufferedWriter(new OutputStreamWriter(
      new FileOutputStream(fileName, true), "utf-8")); 
     thread.start(); 
    } 

    private class LoggingThread implements Runnable { 

     @Override 
     public void run() { 

      try { 
       for (;;) { 
        ThreadedLog.this.writer.write(queue.take() + "\r\n"); 
        ThreadedLog.this.writer.flush(); 
       } 
      } catch (InterruptedException | IOException e) { 
       e.printStackTrace(); 
       try { 
        ThreadedLog.this.writer.close(); 
       } catch (Exception ex) { 
       } 
      } 
     } 
    } 

    public void log(String string) throws InterruptedException { 
     queue.put(string); 
    } 
} 
+0

興味深い。私は、この方法の利点は、スレッドがロックを解放するのを待つ時間を費やさなくて済むということだと思います。私は他の方法よりも不利な点はないと思いますか? –

+0

欠点が1つあります。アプリケーションまたはサーバーがクラッシュし、まだキューに記録されているものがある場合、それらは失われます。あなたがそれで生きることができれば、これは非常に良い解決策かもしれません。 – Alex

関連する問題