为什么Java文件写入消耗CPU?

时间:2018-04-18 10:18:38

标签: java queue bufferedwriter

我在一个单独的线程上使用队列将数据写入文件,但是该过程占用了大约25%的CPU,如此测试主程序所示。

我有什么办法可以解决这个问题吗?

也许我应该在某个地方使用flush()?

测试显示main方法启动并运行队列线程,然后将创建的数据发送给它。队列线程将数据写入BufferedWriter,后者处理将数据写入文件。

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import uk.co.moonsit.utils.timing.Time;

public class OutputFloatQueueReceiver extends Thread {
private static final Logger LOG = Logger.getLogger(OutputFloatQueueReceiver.class.getName());

private ConcurrentLinkedQueue<List<Float>> queue = null;
private boolean running = true;
private final BufferedWriter outputWriter;
private int ctr = 0;

private final int LIMIT = 1000;

public OutputFloatQueueReceiver(String outputFile, String header, ConcurrentLinkedQueue<List<Float>> q) throws IOException {

    queue = q;

    File f = new File(outputFile);
    FileWriter fstream = null;
    if (!f.exists()) {
        try {
            f.getParentFile().mkdirs();
            if (!f.createNewFile()) {
                throw new IOException("Exception when trying to create file " + f.getAbsolutePath());
            }

            fstream = new FileWriter(outputFile, false);
        } catch (IOException ex) {
            //Logger.getLogger(ControlHierarchy.class.getName()).log(Level.SEVERE, null, ex);
            throw new IOException("Exception when trying to create file " + f.getAbsolutePath());
        }
    }

    fstream = new FileWriter(outputFile, true);
    outputWriter = new BufferedWriter(fstream);
    outputWriter.append(header);

}

public synchronized void setRunning(boolean running) {
    this.running = running;
}

@Override
public void run() {

    while (running) {
        while (queue.peek() != null) {
            if (ctr++ % LIMIT == 0) {
                LOG.log(Level.INFO, "Output Queue size = {0} '{'ctr={1}'}'", new Object[]{queue.size(), ctr});
            }

            List<Float> list = queue.poll();
            if (list == null) {
                continue;
            }
            try {
                StringBuilder sbline = new StringBuilder();
                Time t = new Time(list.get(0));
                sbline.append(t.HMSS()).append(",");
                for (Float f : list) {
                    sbline.append(f).append(",");
                }
                sbline.append("\n");
                outputWriter.write(sbline.toString());

            } catch (IOException ex) {
                LOG.info(ex.toString());
                break;
            }
        }
    }
    if (outputWriter != null) {
        try {
            outputWriter.close();
            LOG.info("Closed outputWriter");
        } catch (IOException ex) {
            Logger.getLogger(OutputFloatQueueReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

public static void main(String[] args) {

    try {

        String outputFile = "c:\\tmp\\qtest.csv";
        File f = new File(outputFile);
        f.delete();

        StringBuilder header = new StringBuilder();
        header.append("1,2,3,4,5,6,7,8,9");
        header.append("\n");
        ConcurrentLinkedQueue<List<Float>> outputQueue = null;

        OutputFloatQueueReceiver outputQueueReceiver = null;
        outputQueue = new ConcurrentLinkedQueue<>();

        outputQueueReceiver = new OutputFloatQueueReceiver(outputFile, header.toString(), outputQueue);
        outputQueueReceiver.start();

        for (int i = 1; i < 100000; i++) {
            List<Float> list = new ArrayList<>();

            //list.set(0, (float) i); // causes exception
            list.add((float) i);
            for (int j = 1; j < 10; j++) {
                list.add((float) j);
            }

            outputQueue.add(list);                                
        }

        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            Logger.getLogger(OutputFloatQueueReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }

        outputQueueReceiver.setRunning(false);

    } catch (IOException ex) {
        Logger.getLogger(OutputFloatQueueReceiver.class.getName()).log(Level.SEVERE, null, ex);
    }

}

}

1 个答案:

答案 0 :(得分:1)

这段代码是您的代码使用如此多CPU的原因:

while (running) {
    while (queue.peek() != null) {
        // logging
        List<Float> list = queue.poll();
        if (list == null) {
            continue;
        }
        // do stuff with list
    }
}

基本上,你的代码忙着等待,反复“偷看”,直到队列条目可用。它可能在紧密的循环中旋转。

您应该使用BlockingQueue替换您的队列类,只需使用take() ......就像这样:

while (running) {
    List<Float> list = queue.take();
    // do stuff with list
}

take()无限期调用块,仅在有可用元素时返回,并返回该元素作为结果。如果无限期阻塞是一个问题,你可以使用poll(...)超时,或者你可以安排其他一些线程中断被阻塞的线程。