2011-01-05 55 views
2

我試圖編寫一個文件監視器來檢查文件是否附加了新行,監視器實際上是一個線程,它將始終通過randomaccessfile讀取行。如何重新啓動線程

這是顯示器的核心代碼:

public class Monitor { 
    public static Logger         log    = Logger.getLogger(Monitor.class); 
    public static final Monitor    instance  = new Monitor(); 
    private static final ArrayList<Listener> registers  = new ArrayList<Listener>(); 

    private Runnable          task   = new MonitorTask(); 
    private Thread           monitorThread = new Thread(task); 
    private boolean           beStart   = true; 

    private static RandomAccessFile       raf    = null; 
    private File           monitoredFile = null; 
    private long           lastPos; 

    public void register(File f, Listener listener) { 
     this.monitoredFile = f; 
     registers.add(listener); 
     monitorThread.start(); 
    } 

    public void replaceFile(File newFileToBeMonitored) { 
     this.monitoredFile = newFileToBeMonitored; 

     // here,how to restart the monitorThread? 
    } 

    private void setRandomFile() { 
     if (!monitoredFile.exists()) { 
      log.warn("File [" + monitoredFile.getAbsolutePath() 
        + "] not exist,will try again after 30 seconds"); 
      try { 
       Thread.sleep(30 * 1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      setRandomFile(); 
      return; 
     } 
     try { 
      if (raf != null) { 
       raf.close(); 
       lastPos = 0; 
      } 
      raf = new RandomAccessFile(monitoredFile, "r"); 
      log.info("monitor file " + monitoredFile.getAbsolutePath()); 
     } catch (FileNotFoundException e) { 
      // The file must exist now 
     } catch (IOException e) {} 
    } 

    private void startRead() { 
     beStart = true; 
     String line; 
     while (beStart) { 
      try { 
       raf.seek(lastPos); 
       while ((line = raf.readLine()) != null) { 
        fireEvent(new FileEvent(monitoredFile.getAbsolutePath(), 
          line)); 
       } 
       lastPos = raf.getFilePointer(); 
      } catch (IOException e1) {} 
     } 
    } 

    private void stopRead() { 
     this.beStart = false; 
    } 

    private void fireEvent(FileEvent event) { 
     for (Listener lis : registers) { 
      lis.lineAppended(event); 
     } 
    } 

    private class MonitorTask implements Runnable { 
     @Override 
     public void run() { 
      stopRead(); 

      //why putting the resetReandomAccessFile in this thread method is that it will sleep if the file not exist. 
      setRandomFile(); 
      startRead(); 
     } 

    } 

} 

這是一些幫助類:

public interface Listener { 
    void lineAppended(FileEvent event); 
} 


public class FileEvent { 
    private String line; 
    private String source; 

    public FileEvent(String filepath, String addedLine) { 
     this.line = addedLine; 
     this.source = filepath; 
    } 
    //getter and setter 

} 

這是一個例子,呼籲顯示器:

public class Client implements Listener { 
    private static File f = new File("D:/ab.txt"); 

    public static void main(String[] args) { 
     Monitor.instance.register(f, new Client()); 
     System.out.println(" I am done in the main method"); 
     try { 
      Thread.sleep(5000); 
      Monitor.instance.replaceFile(new File("D:/new.txt")); 
     } catch (InterruptedException e) { 
      System.out.println(e.getMessage()); 
     } 
    } 

    @Override 
    public void lineAppended(FileEvent event) { 
     String line = event.getLine(); 
     if (line.length() <= 0) 
      return; 
     System.err.println("found in listener:" + line + ":" + line.length()); 
    } 
} 

現在,我的問題是代碼工作好,如果我只是打電話:

Monitor.instance.register(file,listener); 

這將監視文件的行追加,並通知監聽器。

然而,當我打電話了不起作用:

Monitor.instance.replaceFile(anotherfile); 

這意味着我要監視另一個文件,而不是之前。

所以在我的監視器,我不得不重新啓動線程,如何使它?

我曾嘗試:

monitorThread.interruppt(); 

它不工作堅持。

任何人都可以爲我解決它或告訴我該怎麼辦?

謝謝。

在我詢問之前,我使用了googling的「重啓java線程」,所以我知道一個不能重啓死線程,但是我的線程沒有返回,所以我認爲它可以重啓。

回答

1

我沒有解釋,而是編寫了一個框架示例。我沒有對它進行非常好的測試,但它可能有些用處。

爲了監視(nother)文件,只需創建一個新的Monitor,並將其傳遞給ScheduledExecutorService。啓動和停止監視非常簡單。你可以(應該)爲多個顯示器重複使用相同的執行程序。

import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ScheduledExecutorService; 
import java.util.concurrent.TimeUnit; 

public interface Event 
{ 
} 

public interface Listener 
{ 
    void handle(Event event); 
} 

public class Monitor 
{ 
    private static final int CHECK_EVERY_SECONDS = 10; 
    private static final int RECHECK_AFTER_IF_NOT_EXISTS_SECONDS = 30; 

    private File file; 
    private ScheduledExecutorService executor; 
    private boolean active; 
    private List<Listener> listeners; 

    public Monitor(File file, ScheduledExecutorService executor) 
    { 
     super(); 
     this.file = file; 
     this.executor = executor; 
     listeners = new ArrayList<Listener>(); 
    } 

    public synchronized void start() 
    { 
     if (active) 
     { 
      return; 
     } 
     active = true; 
     executor.execute(new Runnable() 
     { 
      public void run() 
      { 
       synchronized (Monitor.this) 
       { 
        if (!active) 
        { 
         System.out.println("not active"); 
         return; 
        } 
       } 
       if (!file.exists()) 
       { 
        System.out.println("does not exist, rescheduled"); 
        executor.schedule(this, RECHECK_AFTER_IF_NOT_EXISTS_SECONDS, TimeUnit.SECONDS); 
        return; 
       } 
       Event event = doStuff(file); 
       System.out.println("generated " + event); 
       updateListeners(event); 
       System.out.println("updated listeners and rescheduled"); 
       executor.schedule(this, CHECK_EVERY_SECONDS, TimeUnit.SECONDS); 
      } 
     }); 
    } 

    private Event doStuff(final File file) 
    { 
     return new Event() 
     { 
      public String toString() 
      { 
       return "event for " + file; 
      } 
     }; 
    } 

    public synchronized void stop() 
    { 
     active = false; 
    } 

    public void addListener(Listener listener) 
    { 
     synchronized (listeners) 
     { 
      listeners.add(listener); 
     } 
    } 

    public void removeListener(Listener listener) 
    { 
     synchronized (listeners) 
     { 
      listeners.remove(listener); 
     } 
    } 

    private void updateListeners(Event event) 
    { 
     synchronized (listeners) 
     { 
      for (Listener listener : listeners) 
      { 
       listener.handle(event); 
      } 
     } 
    } 

    public static void main(String[] args) throws IOException 
    { 
     ScheduledExecutorService executor = Executors.newScheduledThreadPool(4); 
     File file = new File("test.png"); 
     Monitor monitor = new Monitor(file, executor); 
     monitor.addListener(new Listener() 
     { 
      public void handle(Event event) 
      { 
       System.out.println("handling " + event); 
      } 
     }); 
     monitor.start(); 
     System.out.println("started..."); 
     System.in.read();  
     monitor.stop(); 
     System.out.println("done"); 
     executor.shutdown(); 
    } 

} 
+0

謝謝,我會試一試。 – hguser 2011-01-05 14:41:04

4

你不重新啓動一個線程,而不是創建一個新的要啓動一個線程每次。

更好的選擇可能是使用Executors.newCachedThreadPool(),它給你一個線程池,將爲你啓動/回收。

BTW:您正在使用遞歸而不是循環來輪詢文件是否存在。使用遞歸可能意味着如果您等待太久,它會拋出一個StackOverflowError。恕我直言,你不應該等待,輪詢線程應該反覆嘗試打開文件,直到它被告知停止(或文件出現)

你目前的實現還意味着如果文件被替換,你將不得不無論如何,在後臺線程中重新打開文件。

+0

其實你說的就是我想要的。但我現在不知道如何使用executeService,哪個邏輯應該放在線程方法中? – hguser 2011-01-05 10:20:27

+0

您可以將任務(可運行)提交()到池中。當他們完成線程回到游泳池並退出時,如果不用一分鐘。根據需要添加新線程。 – 2011-01-05 13:42:30

+0

謝謝你的好意。這是我的新代碼。 http://dpaste.de/8Z6N/然而,我不能在提交新任務時取消以前的任務。 – hguser 2011-01-05 14:40:17

0

Java中的線程無法重新啓動。每當你需要重新啓動線程,你必須必須做一個新的。

這就是說,你可能想看看:

private void setRandomFile() { 
     if (!monitoredFile.exists()) { 
      log.warn("File [" + monitoredFile.getAbsolutePath() 
        + "] not exist,will try again after 30 seconds"); 
      try { 
       Thread.sleep(30 * 1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      setRandomFile(); 
      return; 
     } 
// .... 
} 

這裏你睡30秒,如果文件不存在,那麼遞歸調用相同的功能。現在,我不知道你有什麼業務需求,但是如果這個遞歸足夠長,你將會用完堆棧空間。也許你會得到更好的服務與一個while循環或更好,像Semaphore一點點同步。