多線程并發(fā)思考--文件加鎖
在最近的工作中,經(jīng)常要用到線程,就對(duì)線程相關(guān)知識(shí)稍微看了看,知道并發(fā)線程經(jīng)常引起共享資源沖突,java以提供關(guān)鍵字synchronized的形式,為防止資源沖突提供了內(nèi)置支持.
可是在工作中,我卻碰到了這樣的需求,定時(shí)拋出線程讀寫(xiě)某文件的內(nèi)容,由于相隔時(shí)間很短,我突然想到,會(huì)不會(huì)在第二次輪循開(kāi)始對(duì)該文件進(jìn)行讀操作的時(shí)候,第一次拋出的線程還在對(duì)該文件進(jìn)行寫(xiě)操作,如果有可能,那么第二次讀出的數(shù)據(jù)會(huì)是什么樣的呢?
懷著這樣的疑問(wèn),我開(kāi)始以程序作實(shí)驗(yàn),代碼如下:
1.用于寫(xiě)文件的線程
package chb.thread;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;


/** *//**
* @author 崔紅保
*
* 這個(gè)線程用于寫(xiě)文件
*/

public class Thread_writeFile extends Thread...{

public void run()...{
Calendar calstart=Calendar.getInstance();
File file=new File("D:/test.txt");

try ...{
if(!file.exists())
file.createNewFile();
FileWriter fw=new FileWriter(file);
BufferedWriter bw=new BufferedWriter(fw);

for(int i=0;i<1000;i++)...{
sleep(10);
bw.write("這是第"+(i+1)+"行,應(yīng)該沒(méi)錯(cuò)哈 ");
}
bw.close();
bw=null;
fw.close();
fw=null;

} catch (IOException e) ...{
e.printStackTrace();

} catch (InterruptedException e) ...{
e.printStackTrace();
}
Calendar calend=Calendar.getInstance();
System.out.println("寫(xiě)文件共用了"+(calend.getTimeInMillis()-calstart.getTimeInMillis())+"毫秒");
}

}

2.用于讀文件的線程
package chb.thread;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Calendar;


/** *//**
* @author 崔紅保
*
*這個(gè)線程用于讀文件
*/

public class Thread_readFile extends Thread...{

public void run()...{

try ...{
Calendar calstart=Calendar.getInstance();
sleep(5000);
File file=new File("D:/test.txt");
BufferedReader br=new BufferedReader(new FileReader(file));
String temp=null;
temp=br.readLine();

while(temp!=null)...{
System.out.println(temp);
temp=br.readLine();
}
br.close();
br=null;
Calendar calend=Calendar.getInstance();
System.out.println("讀文件共用了"+(calend.getTimeInMillis()-calstart.getTimeInMillis())+"毫秒");

}catch (FileNotFoundException e) ...{
e.printStackTrace();

} catch (IOException e) ...{
e.printStackTrace();

} catch (InterruptedException e) ...{
e.printStackTrace();
}
}
}

3.分別啟用兩個(gè)線程
Thread_writeFile thf3=new Thread_writeFile();
Thread_readFile thf4=new Thread_readFile();
thf3.start();
thf4.start();
4.結(jié)果分析
雖然寫(xiě)文件的操作開(kāi)始5秒鐘后,讀文件的操作才開(kāi)始進(jìn)行,可是讀文件的線程并沒(méi)有讀出數(shù)據(jù),改變時(shí)間,讀出的數(shù)據(jù)也就各不相同.
為了避免以上結(jié)果,我們希望在一個(gè)線程在操作某個(gè)文件的時(shí)候,其他線程不能對(duì)該文件進(jìn)行讀或?qū)懖僮?要怎么才能實(shí)現(xiàn)呢?利用java提供的synchronized似乎無(wú)法完成,因?yàn)槊總€(gè)線程是在程序中動(dòng)態(tài)拋出的.郁昧了一天之后,我終于找到了一個(gè)解決辦法,就是利用java.nio包中的FileChannel對(duì)文件進(jìn)行加鎖.
具體實(shí)現(xiàn)方法如下:
1.寫(xiě)文件的線程
package chb.thread;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Calendar;


/** *//**
* @author chb
*
*/

public class Thread_writeFile extends Thread...{

public void run()...{
Calendar calstart=Calendar.getInstance();
File file=new File("D:/test.txt");

try ...{
if(!file.exists())
file.createNewFile();
//對(duì)該文件加鎖
FileOutputStream out=new FileOutputStream(file,true);
FileChannel fcout=out.getChannel();
FileLock flout=null;

while(true)...{
flout=fcout.tryLock();

if(flout!=null)...{
break;
}

else...{
System.out.println("有其他線程正在操作該文件,當(dāng)前線程休眠1000毫秒");
sleep(100);
}
}

for(int i=1;i<=1000;i++)...{
sleep(10);
StringBuffer sb=new StringBuffer();
sb.append("這是第"+i+"行,應(yīng)該沒(méi)啥錯(cuò)哈 ");
out.write(sb.toString().getBytes("utf-8"));
}

flout.release();
fcout.close();
out.close();
out=null;

} catch (IOException e) ...{
e.printStackTrace();

} catch (InterruptedException e) ...{
e.printStackTrace();
}
Calendar calend=Calendar.getInstance();
System.out.println("寫(xiě)文件共花了"+(calend.getTimeInMillis()-calstart.getTimeInMillis())+"秒");
}
}

2.讀文件的線程
package chb.thread;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Calendar;


/** *//**
* @author chb
* ?????
*/

public class Thread_readFile extends Thread...{

public void run()...{

try ...{
Calendar calstart=Calendar.getInstance();
sleep(5000);
File file=new File("D:/test.txt");
//給該文件加鎖
FileInputStream fis=new FileInputStream(file);
FileChannel fcin=fis.getChannel();
FileLock flin=null;

while(true)...{
flin=fcin.tryLock(0,Long.MAX_VALUE,true);

if(flin!=null)...{
break;
}

else...{
System.out.println("有其他線程正在操作該文件,當(dāng)前線程休眠1000毫秒");
sleep(1000);
}
}
byte[] buf = new byte[1024];
StringBuffer sb=new StringBuffer();

while((fis.read(buf))!=-1)...{
sb.append(new String(buf,"utf-8"));
buf = new byte[1024];
}
System.out.println(sb.toString());
flin.release();
fcin.close();
fis.close();
fis=null;
Calendar calend=Calendar.getInstance();
System.out.println("讀文件共花了"+(calend.getTimeInMillis()-calstart.getTimeInMillis())+"秒");

}catch (FileNotFoundException e) ...{
e.printStackTrace();

} catch (IOException e) ...{
e.printStackTrace();

} catch (InterruptedException e) ...{
e.printStackTrace();
}
}
}

3.分別啟用兩個(gè)線程
Thread_writeFile thf3=new Thread_writeFile();
Thread_readFile thf4=new Thread_readFile();
thf3.start();
thf4.start();
4.結(jié)果分析
以上程序在對(duì)一個(gè)文件執(zhí)行寫(xiě)操作前,先對(duì)該文件加鎖,這樣其他線程就不能再對(duì)該文件操作,等該線程的寫(xiě)操作結(jié)束,釋放資源,其他線程才可以繼續(xù)對(duì)該文件執(zhí)行相應(yīng)的讀寫(xiě)操作.
可是,郁昧的是,這段程序在windows下可以正確執(zhí)行,在linux下卻無(wú)效.根據(jù)<Thinking in Java>上的觀點(diǎn)是:對(duì)獨(dú)占鎖或者共享鎖的支持必須由底層的操作系統(tǒng)提供.
綜觀我的解決方法,總感覺(jué)不太完美,各位如有好的方法來(lái)判斷一個(gè)文件是否正被某個(gè)線程使用,希望大家一起分享一下.
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1440226