有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java:每隔几秒创建一个文件

我正在使用ScheduledThreadPoolExecutor每隔fileIntervalInSeconds创建一个文件:

executorService = new ScheduledThreadPoolExecutor(1);
        executorService.scheduleAtFixedRate(new Runnable()
        {
            @Override
            public void run()
            {
                    File file = new File(fileName);
                    if (file.exists())
                    {
                        Log.debug("creating new file");
                        openFileWriter(file);
                    }

            }
        }, fileIntervalInSeconds, fileIntervalInSeconds, TimeUnit.SECONDS);
    }
private void openFileWriter() throws FileSystemNotificationException
{

        // 1 - close exist writer
        writer.close();
        // 2 - rename to backup file name
          ...
        // 3 - create new file      
        FileWriter writerFile = new FileWriter(fileName, true);
        writer = new PrintWriter(writerFile);

}

我一直在给文件写警告信息:

private synchronized void writeLine(String line) throws InterruptedException
{
    writer.println(line);
}

我的问题是:

  1. 当writer未关闭时,如何确保我正在使用它?(writer.close())
  2. 如何等待ScheduledThreadPoolExecutor完成文件创建,然后再开始写入

共 (4) 个答案

  1. # 1 楼答案

    您只需在write方法中每小时创建一个新文件。你会有一些轻微的时间检查开销,但这应该可以忽略不计。下面的示例将每小时创建一个新的日志文件,并在文件名前面添加以毫秒为单位的时间。你可以根据自己的情况设定时间

    public class LogWriter {
        private long lastCreationTime;
        PrintWriter writer;
        String logFileName;
    
        public LogWriter(String logFileName) {
            this.logFileName = logFileName;
            createLogFile(logFileName);
        }
    
        private void createLogFile(String fileName) {
            if(writer != null) {
                writer.close();
            }
            lastCreationTime = System.currentTimeMillis();
            FileWriter writerFile;
            try {
                writerFile = new FileWriter(lastCreationTime + "_" + fileName, true);
                writer = new PrintWriter(writerFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        private synchronized void writeLine(String line) {
            if(lastCreationTime < System.currentTimeMillis() - 3600000) {
                createLogFile(logFileName);
            }
            writer.write(line);
        }
    }
    
  2. # 2 楼答案

    在写入之前检查文件是否存在如何。不需要反向绑定线程或同步

    private synchronized void writeLine(String line) {
        if (!file.exists())
           reopenWritingFile();
        writer.println(line);
    }
    
  3. # 3 楼答案

    让一个单独的线程来处理日志记录,而不是那个相当复杂的构造,怎么样

    public class Logger extends Thread {
    
    private final LinkedBlockingQueue<String> linesToWrite = new LinkedBlockingQueue<>();
    private final String filename;
    
    private Logger(String filename) {
      super("Logging thread");
      this.filename = filename;
      this.setDaemon(true);
      this.setPriority(Thread.MIN_PRIORITY);
    }
    
    @Override
    public void run() {
      try (BufferedWriter out = new BufferedWriter(new FileWriter(filename, true))) {
    
        String line;
        while (this.isInterrupted() == false) {
          line = linesToWrite.take();
          out.write(line);
          out.newLine();
          out.flush();
        }
      } catch (InterruptedException e) {
      } catch (IOException ex) {
        System.out.println("Failed to access log file: " + ex);
      }
    }
    
    public void log(final String line) {
      this.linesToWrite.add(line);
    }
    

    然后初始化记录器一次:

    final Logger logger = new Logger("test.log");
    logger.start();
    

    然后你可以在任何地方以线程安全的方式使用它,比如:

    logger.log("Test message");
    

    您不需要停止记录器,因为Java将使用try构造确保文件正确关闭。但是,如果你愿意,你可以这样阻止它:

    logger.interrupt();
    

    现在,您可以通过单线程方式执行所有文件操作,因为任何时候只有一个线程访问日志文件

  4. # 4 楼答案

    你有两个选择:

    1. 在关闭旧的编写器之前创建新的编写器
    2. 在关闭书写器之前建立一个锁,在书写时进行检查

    例如:

    volatile PrintWriter writer;
    ReadWriteLock lock = new ReentrantReadWriteLock();
    Lock writeLock = lock.writeLock();
    Lock readLock = lock.readLock();
    
    private void openFileWriterWithLock() throws IOException {
    
      if (writeLock.tryLock()) {
        try {
          // 1 - close exist writer
          writer.close();
          // 2 - rename to backup file name
          //...
          // 3 - create new file      
          FileWriter writerFile = new FileWriter(fileName, true);
          writer = new PrintWriter(writerFile);
        } finally {
          writeLock.unlock();
        }
      }
    
    }
    
    private synchronized void writeLineWithLock(String line) throws InterruptedException {
      readLock.lock();
      try {
        writer.println(line);
      } finally {
        readLock.unlock();
      }
    }
    
    private void openFileWriterWithoutLock() throws IOException {
      // 0. Note old file.
      PrintWriter oldWriter = writer;
      // 1. Create new file.
      FileWriter writerFile = new FileWriter(fileName, true);
      // 2. Swap the new one in.
      writer = new PrintWriter(writerFile);
      // 3 - close old writer
      oldWriter.close();
    }
    
    private synchronized void writeLineWithoutLock(String line) throws InterruptedException {
      writer.println(line);
    }