2017-05-11 28 views
0

ファイルシステムに複数の項目を書き込み、あるスレッドが別のスレッドのデータを上書きしたり、別の項目のデータとインターリーブしたりすることを完全に期待しています。Javaの同時ファイル書き込み - 失敗する必要があります

ただし、次のコードは予期せず実行されます。

あるスレッドのデータが別のスレッドのデータを上書きしないのはなぜですか?すべてのスレッドは1人のライターを共有します。コードがJVM実装の詳細のために渡されるか、個々の項目を混ぜ合わせないことが本当に期待できるか?

私は、複数のスレッドが同じファイルに書き込むといういくつかのクエストを見たことがありますが、これはパフォーマンスの最適化に関するものでした。インポートスタイルは、投稿時の簡潔さのためだけのものです。

package com.test; 

import static org.junit.Assert.assertEquals; 

import java.io.*; 
import java.nio.charset.*; 
import java.nio.file.*; 
import java.util.*; 

import org.springframework.boot.CommandLineRunner; 

import com.fasterxml.jackson.annotation.JsonProperty; 
import com.fasterxml.jackson.core.JsonProcessingException; 
import com.fasterxml.jackson.databind.MappingIterator; 
import com.fasterxml.jackson.databind.ObjectMapper; 

public class DiskWriterApplication implements CommandLineRunner { 

    public static void main(String[] args) throws Exception { 
     new DiskWriterApplication().run(args); 
    } 

    @Override 
    public void run(String... args) throws Exception { 
     Path path = Paths.get(System.getProperty("user.home")+"/java-file.txt"); 
     if (!Files.exists(path)) { 
      Files.createFile(path); 
     } else { 
      Files.delete(path); 
      Files.createFile(path); 
     } 
     BufferedWriter writer = Files.newBufferedWriter(path, Charset.forName("UTF-8"), StandardOpenOption.APPEND); 


     Thread[] threads = new Thread[4]; 

     for (int i=0; i< 4; i++) { 
      threads[i] = new Thread(new DataWriter(writer, createDataItems(i))); 
     } 

     Arrays.asList(threads).forEach(Thread::start); 
     Arrays.asList(threads).forEach(t-> { 
      try { 
       t.join(); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     }); 
     writer.close(); 

     //Verify Lines were written correctly 
     ObjectMapper mapper = new ObjectMapper(); 
     MappingIterator<Data> valueIterator = mapper.readerFor(Data.class).readValues(Files.newInputStream(path)); 

     Set<String> uniqueItems = new HashSet<>(); 
     int[] groupItemCount = new int[4]; 
     while (valueIterator.hasNext()) 
     { 
      Data item = valueIterator.next(); 

      assertEquals("First Item and second Item should be equal", item.firstValue, item.secondValue); 
      assertEquals(10, item.innerObject.size()); 
      assertEquals(20, item.listValues.size()); 

      for (int i = 0 ; i< 10; i++) { 
       assertEquals(item.firstValue, item.innerObject.get("innerProp"+i)); 
      } 
      for (int i = 0 ; i< 20; i++) { 
       assertEquals(item.firstValue, item.listValues.get(i)); 
      } 
      uniqueItems.add(item.firstValue); 
      groupItemCount[item.group]++; 
     } 

     System.out.println("Got " + uniqueItems.size() + " uniqueItems"); 
     assertEquals("Should be 4000 uniqueItems", 4000, uniqueItems.size()); 
     assertEquals("Should be 1000 items in group[0]", 1000, groupItemCount[0]); 
     assertEquals("Should be 1000 items in group[1]", 1000, groupItemCount[1]); 
     assertEquals("Should be 1000 items in group[2]", 1000, groupItemCount[2]); 
     assertEquals("Should be 1000 items in group[3]", 1000, groupItemCount[3]); 
    } 



    private List<Data> createDataItems(int groupNumber) { 
     List<Data> items = new ArrayList<>(); 
     for (int i =0; i<1000; i++) { 
      Data item = new Data(); 
      item.group = groupNumber; 
      item.itemNumber = i; 
      item.firstValue = "{group" + groupNumber + "item" + i + "}"; 
      item.secondValue = "{group" + groupNumber + "item" + i + "}"; 
      for (int j =0; j< 10; j ++) { 
       item.addInnerProperty("innerProp"+j , "{group" + groupNumber + "item" + i + "}"); 
      } 
      for (int j=0; j<20; j++) { 
       item.addListValue("{group" + groupNumber + "item" + i + "}"); 
      } 
      items.add(item); 
     } 
     return items; 
    } 


    private class DataWriter implements Runnable { 
     private ArrayList<String> data; 
     private PrintWriter writer; 

     public DataWriter(BufferedWriter writer, List<Data> items) { 
      this.writer = new PrintWriter(writer); 
      this.data = new ArrayList<String>(); 

      ObjectMapper mapper = new ObjectMapper(); 

      for (Data i : items) { 
       try { 
        String stringValue = mapper.writeValueAsString(i); 
        data.add(stringValue); 
       } catch (JsonProcessingException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
     } 


     @Override 
     public void run() { 
      System.out.println("Starting batch"); 
      data.forEach(t -> { 
        writer.println(t); 
        writer.flush(); 
        }); 
      System.out.println("finishing batch"); 
     } 
    } 

    public static class Data { 
     public int itemNumber; 
     public int group; 
     @JsonProperty 
     private String firstValue; 
     @JsonProperty 
     private String secondValue; 
     @JsonProperty 
     private Map<String, String> innerObject = new HashMap<>(); 
     @JsonProperty 
     private List<String> listValues = new ArrayList<>(); 

     public void addInnerProperty(String key, String value){ 
      this.innerObject.put(key, value); 
     } 

     public void addListValue(String value) { 
      this.listValues.add(value); 
     } 

    } 
} 
+0

同期とスレッドセーフですか? – EJP

+0

私はそれが失敗することを期待しました。申し訳ありませんがおそらく明らかではありませんでした。 – Wes

+0

、ユーザーごとに3585566おそらくhttp://stackoverflow.com/questions/30080560/is-writting-on-file-using-bufferwriter-initialized-by-filewriter-thread-safe-またはその質問に答えます。 – Wes

答えて

1

あなたが他の人にもわかるように、同じことを尋ねるスレッド: Writing a file using multiple threads in java Is writting on file using bufferwriter initialized by filewriter thread safe or not?

は、BufferedWriterのは、どこにそれが失敗すると言うん

+0

その最初の質問は反対であると言った。それは私が期待していたデータの破損を持っていました。 – Wes

+0

私はこの質問に答えるかもしれないように見えますが、もう1つは見えますが。 – Wes

+1

マルチスレッドで作業する場合は、[BlockingQueues](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html)を使用することをお勧めします。この方法では、ファイルへの単一のスレッド書込みがありますが、複数のスレッドが同じキューを共有して潜在的な問題を回避できます – Robert

関連する問題