作家是一种原子方法吗?

时间:2012-03-01 07:46:57

标签: java multithreading file-io atomic

我想在文件中写一些字符串。所以,我使用了 BufferedWriter 类。由于许多线程倾向于写入该文件,因此我想知道 write writeLine 方法是否是原子的。

另外,我希望程序将输出写入多个文件,每个文件100行(比如file.txt0,file.txt1,...)。例如

public class Main {
    static ExecutorService exec = Executors.newFixedThreadPool(5);
    BufferedWriter bw;
    public class myWriter implements Runnable {
        String str;
        myWriter (String str) {
            this.str = str;
        }
        public void run() {
            bw.write(str);
            bw.writeLine();
        }
    }
    public static void main(String[] args) {
        bw = new BufferedWriter(new  FileWriter("train.txt"));
        for (String arg: args)
            exec.execute(new myWriter(arg));
        exec.awaitTermination(100000, TimeUnit.MILLISECONDS);
    }
}

有人能帮助我吗?如果它们不是原子的,我怎样才能使它们成为原子并避免碰撞?

5 个答案:

答案 0 :(得分:6)

不,那些不是原子的。

如果要对同一文件进行多次写入,请使用FileLocks

try {
    // Get a file channel for the file
    File file = new File("filename");
    FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

    // Use the file channel to create a lock on the file.
    // This method blocks until it can retrieve the lock.
    FileLock lock = channel.lock();

    // Try acquiring the lock without blocking. This method returns
    // null or throws an exception if the file is already locked.
    try {
        lock = channel.tryLock();
    } catch (OverlappingFileLockException e) {
        // File is already locked in this thread or virtual machine
    }

    // Release the lock
    lock.release();

    // Close the file
    channel.close();
} catch (Exception e) {
}

答案 1 :(得分:5)

以下代码是jdk6的源代码, 这是BufferedWriter中写入的实现,因为函数体中有synchronized,我认为write()中的BufferedWriter是线程安全的。顺便说一下,write(String)是通过调用write(String,int,int)来实现的。

public void write(String s, int off, int len) throws IOException {

    synchronized (lock) {

        ensureOpen();

        int b = off, t = off + len;

        while (b < t) {

            int d = min(nChars - nextChar, t - b);
            s.getChars(b, b + d, cb, nextChar);
            b += d;
            nextChar += d;

            if (nextChar >= nChars)
                flushBuffer();
            }
        }
    }
}

答案 2 :(得分:3)

您可以使用FileLocks但价格昂贵。

就个人而言,我会使用普通的对象锁。 e.g。

synchronized(bufferedWriter) {
  bufferedWriter.write stuff
  bufferedWriter.write more stuff
}

答案 3 :(得分:1)

NGloom是对的,以下是对BufferredWriter.append()方法(Oracle JDK 7运行时)进行并发访问的程序的一部分的线程转储,该方法在方法体上不使用任何同步。很明显,BufferredWriter.append()的实现在BufferredWriter对象的实例上使用了一个监视器,因此它是线程安全的。但是我找不到任何关于线程安全on the related java doc的内容,因此API没有提供任何此类保证,因为这样的实现可能会有所不同?此外,Writer.write()是线程安全的事实并不会阻止另一个将另一个OutputStream对象包装到同一文件的编写器尝试同时写入它,这是不安全的。

ForkJoinPool-1-worker-3" daemon prio=10 tid=0x00007f358c002800 nid=0x3e66 waiting for monitor entry [0x00007f360bdfb000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.io.BufferedWriter.write(BufferedWriter.java:220)
    - waiting to lock <0x0000000760da06d8> (a java.io.OutputStreamWriter)
    at java.io.Writer.write(Writer.java:157)
    at java.io.Writer.append(Writer.java:227)
    at ...


ForkJoinPool-1-worker-2" daemon prio=10 tid=0x00007f358c001000 nid=0x3e65 waiting for monitor entry [0x00007f360befc000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.io.BufferedWriter.write(BufferedWriter.java:220)
    - waiting to lock <0x0000000760da06d8> (a java.io.OutputStreamWriter)
    at java.io.Writer.write(Writer.java:157)
    at java.io.Writer.append(Writer.java:227)
    at ...

答案 4 :(得分:1)

是的,BufferedWriter是线程安全的。请参阅下面的代码段。编写内容同步块时可确保线程安全。

$csv_datei = "applicationData.csv";
$feler_trenner = ";";
$zeilen_trenner = "n";
if (@file_exists($csv_datei) == false) {
    echo 'Die CSV Datei: '. $csv_datei.' gibt es nicht!';
}

else {
    $datei_inhalt = @file_get_contents($csv_datei);
    $zeilen = explode($zeilen_trenner, $datei_inhalt);

    if (is_array($zeilen) == true) {
        foreach($zeilen as $zeile) {
           $felder = explode($feler_trenner, $zeile);
           $i = 0;
           if (is_array($felder) == true) {
                foreach($felder as $felde) {
                    if ($felde != '') {
                        echo (($i != 0) ? ', ':'') . str_replace('"', '', $felde);
                        $i++;
                    }
                }
            }
            echo '<br>';
        }
    }
}

enter image description here