2012-05-13 3 views
0

두 개의 다른 텍스트 파일에 쓰는 데 필요한 여러 스레드가 있습니다.정적 클래스에서 파일 출력 동기화

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

이 동시에 같은 파일에 쓰기 작업을 수행하는 하나 개의 스레드가 아닌 더 이상 보장하지의 수용 방법입니다 지금까지 나는이 코드를 가지고?

스레드가 이러한 메서드 중 하나를 호출하고 sync 블록을 호출하면 다른 스레드가 함께오고 동일한 메서드를 실행하려고하면 어떻게됩니까? 로컬 변수 writer을 사용하려고하면 다른 스레드에 의해 잠겨져 같은 블록을 잠그려고 시도 할 것인가? 나는 단순히 그것의 자신의 분리 된 변수를 만들 것이라고 생각했을 것이다. 그것은 작가에게 대신 정적 클래스 변수를 만들어야한다는 것을 의미 할까? 코드에서 널 포인터 예외입니다

+0

2 개의 다른 파일에 쓰고 있다면 동기화 할 필요가 없습니다. – user845279

+1

''\ r \ n "'-''\ n"'만을 사용하고 런타임이 플랫폼의 올바른 줄 끝을 쓰게하십시오. –

+0

GameLog 및 ServerLog 출력을 조정하거나 단일 출력 줄에서 두 스레드가 인터리빙하지 못하도록하려면 어떻게해야합니까? –

답변

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

public static synchronized void printToGameLog(String value){ 
처럼, 전체 방법은 동기화 설정하는 정적 메소드

synchronized(Logger.class){ 

또는 다른 대안에 동기화 된 블록을 사용하는이 방법이다 시도

public static synchronized void printToServerLog(String value){ 

난 당신이 동기화를 필요가 없습니다 확신 여기에서는 여러 스레드에서 읽기/쓰기중인 상태가있는 경우에만 동기화가 필요합니다.

+0

죄송합니다. 물론 그것은 NPE를 제공 할 것입니다. 나는 그것이 초기화 된 후에 필자와 동기화하기 위해 내 질문을 다시 쓸 것이라고 생각한다. 그러나 당신의 제안으로, 이것은 하나의 쓰레드가 gameLog에 쓰고있는 동안 다른 쓰레드가 serverLog에 쓰는 것을 막을 수 있다는 것을 의미하지 않습니까? –

+0

그래, 그게 네가하고 싶은 일이지만 내가 네 queestion을 잘못 읽었을 때 알았어 – 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

하나의 단점이 있습니다. 응용 프로그램 또는 서버가 충돌하고 여전히 대기열에 기록 될 일부 항목이 있으면 해당 항목이 손실됩니다. 당신이 그걸로 살 수 있다면, 이것은 아주 좋은 해결책 일 것입니다. – Alex