最近被問到Log4j如何通過多個線程向一個檔案中寫入資料,當時沒有一點頭緒, 因為用過windows記事本的小夥伴都知道,當一個記事本檔案被同時打開多次,并且每個都修改,那麼隻會保留第一次打開的内容,這就是互斥鎖的機制吧。
具體場景:三個線程分别讀取三個檔案a,b,c 并向d檔案中寫入
現在想起來兩個方案給小夥伴分享一下:
第一種(不可行)使用IO流複用,就是多個線程共用一個IO流
出現問題:
1.部分檔案内容無法儲存。
2.影響其它線程使用檔案
第二種方案:
思路:既然不能同時讀寫,那麼就一個或多個線程讀取檔案,然後單獨的一個或多個線程寫檔案。
比如 建立三個線程r1,r2,r3分别讀取a,b,c 建立一個線程w1将内容寫入到 d. 那麼讀寫操作就可以同步進行,也不需要IO複用了,隻需要建立一個緩沖區即可。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcVHbtllb1cVWzkTMZZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN0kTNyUzMxEDNwkDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
不多說了,上代碼(因為需要代碼盡量清晰,省略所有io.close())
/**
* 從abc三個檔案中向d中寫
* 多個線程向記憶體數組中讀,讀完之後,由單獨的一個線程寫入到檔案中
*/
public class Main042{
//建立堵塞隊列,用于讀線程的資料存放
public static BlockingQueue<byte[]> queue = new ArrayBlockingQueue<>(1024*1024);
//建立 CountDownLatch,标記讀線程的狀态(是否讀取完成)。
public static CountDownLatch count =new CountDownLatch(3);
public static void main(String[] args){
Read r1= new Read("d:/a.txt",queue,count);
Read r2= new Read("d:/b.txt",queue,count);
Read r3= new Read("d:/c.txt",queue,count);
Write w1 = new Write("d:/d.txt", queue, count);
r1.start();
r2.start();
r3.start();
w1.start();
}
}
class Write extends Thread{
private BlockingQueue<byte[]> buffer;
private FileOutputStream fileOutputStream;
private CountDownLatch count;
public Write(String file,BlockingQueue<byte[]> buffer,CountDownLatch count) {
this.buffer = buffer;
this.count =count;
try {
this.fileOutputStream = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public void run(){
boolean b =true;
while(b) {
try {
fileOutputStream.write(buffer.take());
} catch (Exception e) {
e.printStackTrace();
}
if(buffer.isEmpty()&&count.getCount()==0) {
//當緩沖區沒有元素,并且 count為0,則說明讀寫完成
b =false;
}
}
}
}
//讀線程
class Read extends Thread{
private BlockingQueue<byte[]> buffer;
private FileInputStream fileInputStream;
private CountDownLatch count;
private volatile int i=0;
public Read(String readFileName, BlockingQueue<byte[]> buffer,CountDownLatch count) {
this.count = count;
this.buffer = buffer;
try {
this.fileInputStream = new FileInputStream(readFileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public void run(){
try {
byte[] b = new byte[1024*10];
while((i = fileInputStream.read(b))>=0) {
buffer.put(Arrays.copyOf(b, i));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
count.countDown();//讀線程結束,count--;
}
}
}