@[toc]
NIO提升性能
在JAVA的标准I/O中,提供了基于流的I/O实现,即InputStream和OutputStream。这种基于流的实现以字节为单位处理数据。NIO是New I/O的简称,表示一套新的JAVA I/O标准。在Jdk 1.4中开始引入,它具有以下特性:
- 为所有的原始类型提供(Buffer)缓存支持;
- 使用Java.nio.charset.Charset作为字符集编码解码解决方案;
- 增加通道(Cahnnel)对象,作为新的原始I/O抽象;
- 支持锁和内存映射文件的文件访问接口;
- 提供了基于Selector的异步网络I/O。
在这里插入图片描述与流式的I/O不容,NIO是基于块(Block)的,它以块为基本单位处理数据。在NIO中,最为重要的2个组件是缓冲Buffer和通道Channel。缓冲是一块连续的内存块,是NIO读写数据的中转地。通道表示缓冲数据的源头或者目的地,它用于向缓冲读取或者写入数据,是访问缓冲的接口。
多线程读写同一个文件有哪些场景需要同步处理?
- 有线程正在读文件,另开辟线程写文件;
- 有线程正在写文件,另开辟线程读文件;
- 有线程正在写文件,另开辟线程写文件
总之,读写互斥,写读互斥,写写互斥,只有读读相容(可以异步)。
使用对文件加锁的方式做到线程安全
FileInputStream、FileOutputStream、RandomAccessFile均可得到FileChannel对象,对文件锁进行操作。
独占锁tryLock()
FileChannel的tryLock()是非阻塞的,也就是说,在发现文件被锁住的时候,直接返回null,并且抛出异常,如果没有锁住,直接返回该文件的文件锁。
它是独占锁,就是只能被一个线程持有,它能禁止其他线程获取共享锁,可用于写文件。
while (true) {
try {
fileLock = fileChannel.tryLock();//独占锁
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
}
}
共享锁tryLock(0, Long.MAX_VALUE, true)
FileChannel的tryLock(0, Long.MAX_VALUE, true)是非阻塞的,在发现文件被锁住的时候,直接返回null,并且抛出异常,如果没有锁住,直接返回该文件的文件锁。
它是共享锁,能被多个线程同时持有,它能禁止其他线程获取独占锁,可用于读文件。
while (true) {
try {
fileLock = fileChannel.tryLock(0, Long.MAX_VALUE, true);//共享锁
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
}
}
独占锁lock()
而FileChannel的lock()是阻塞的,在文件被锁定的情况下,会保持阻塞,直到获得该锁为止。
fileLock = fileChannel.lock();
......
写文件线程安全
/**
* 将str写入文件,同步操作,独占锁
*/
public void writeStr2ReplaceFileSync(String str, String pathFile, IOListener ioListener) {
File file;
try {
file = FileUtils.createFile(pathFile);
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail("文件创建失败,请检查路径是否合法以及读写权限");
return;
}
FileOutputStream fileOutputStream = null;
FileChannel fileChannel = null;
FileLock fileLock = null;//文件锁
try {
/**
* 写文件
*/
fileOutputStream = new FileOutputStream(file);
fileChannel = fileOutputStream.getChannel();
while (true) {
try {
fileLock = fileChannel.tryLock();//独占锁
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
}
}
if (fileLock != null) {
int len = 0;
long current = file.length();
if (isRunning ) {
fileChannel.write(ByteBuffer.wrap(str.getBytes()));
current += len;
LogUtils.log("当前线程" + Thread.currentThread().getName());
ioListener.onLoading(str.getBytes(), current, str.length());
}else {
//中断
ioListener.onInterrupted();
}
if (fileLock != null && fileLock.isValid()) {
LogUtils.log("release-write-lock");
fileLock.release();
}
close(fileChannel);
close(fileOutputStream);
if (file.length() == str.getBytes().length) {
ioListener.onCompleted(file);
}
}
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
}
}
读文件线程安全
/**
* 同步读取,共享锁,但无法同时进行写操作
*
* @param ioListener
*/
public void read2StrSync(String pathFile, IOListener ioListener) {
FileInputStream fileInputStream = null;
FileChannel fileChannel = null;
FileLock fileLock = null;//文件锁
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
try {
/**
* 读文件
*/
fileInputStream = new FileInputStream(pathFile);
fileChannel = fileInputStream.getChannel();
while (true) {
try {
fileLock = fileChannel.tryLock(0, Long.MAX_VALUE, true);//共享锁
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
}
}
if (fileLock != null) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
long current = 0;
while (isRunning && (len = fileChannel.read(byteBuffer)) != -1) {
//0,byteBuffer.position(),必须写这个,否则GG,读取文件错乱
byteArrayOutputStream.write(byteBuffer.array(),0,byteBuffer.position());
current += len;
ioListener.onLoading("", current, fileChannel.size());
byteBuffer.clear();
}
if (fileLock != null && fileLock.isValid()) {
LogUtils.log("release-read-lock");
fileLock.release();
}
close(fileChannel);
close(fileInputStream);
//中断
if (len != -1) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted(byteArrayOutputStream.toString("utf-8"));
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
}
}
小编写的IOListener接口,用于回调
public interface IOListener<T> {
public void onCompleted(T result);
public void onLoading(T readedPart, long current, long length);
public void onInterrupted();
public void onFail(String errorMsg);
}
小编写的IOUtils工具类,专门用于文件读写,流的读写
public class IOUtils {
private boolean isRunning = true;
private long contentLength = 0;
private String encodeType = "utf-8";
public IOUtils() {
isRunning = true;
}
public IOUtils setContentLength(long contentLength) {
this.contentLength = contentLength;
return this;
}
public IOUtils setEncodeType(String encodeType) {
this.encodeType = encodeType;
return this;
}
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void stop() {
this.isRunning = false;
}
public void read(boolean isLine, InputStream inputStream, IOListener ioListener) {
if (isLine) {
readLine2String(inputStream, ioListener);
} else {
read2String(inputStream, ioListener);
}
}
/**
* @param ioListener
*/
public void read2String(String pathFile, IOListener ioListener) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(pathFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
return;
}
read2String(fileInputStream, ioListener);
}
public void read2String(InputStream inputStream, IOListener ioListener) {
if (!(inputStream instanceof BufferedInputStream)) {
inputStream = new BufferedInputStream(inputStream);
}
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
try {
inputStreamReader = new InputStreamReader(inputStream, encodeType);
bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int len = 0;
long current = 0;
while (isRunning && (len = bufferedReader.read(buf)) != -1) {
sb.append(buf, 0, len);
current += len;
ioListener.onLoading("", current, contentLength);
}
//中断
if (len != -1) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted(sb.toString());
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
close(bufferedReader);
close(inputStreamReader);
close(inputStream);
}
}
/**
* 同步读取,共享锁,但无法同时进行写操作
*
* @param ioListener
*/
public void read2StrSync(String pathFile, IOListener ioListener) {
FileInputStream fileInputStream = null;
FileChannel fileChannel = null;
FileLock fileLock = null;//文件锁
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
try {
/**
* 读文件
*/
fileInputStream = new FileInputStream(pathFile);
fileChannel = fileInputStream.getChannel();
while (true) {
try {
fileLock = fileChannel.tryLock(0, Long.MAX_VALUE, true);//共享锁
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
}
}
if (fileLock != null) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
long current = 0;
while (isRunning && (len = fileChannel.read(byteBuffer)) != -1) {
//0,byteBuffer.position(),必须写这个,否则GG,读取文件错乱
byteArrayOutputStream.write(byteBuffer.array(),0,byteBuffer.position());
current += len;
ioListener.onLoading("", current, fileChannel.size());
byteBuffer.clear();
}
if (fileLock != null && fileLock.isValid()) {
LogUtils.log("release-read-lock");
fileLock.release();
}
close(fileChannel);
close(fileInputStream);
//中断
if (len != -1) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted(byteArrayOutputStream.toString("utf-8"));
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
}
}
/**
* @param ioListener
*/
public void readLine2String(String pathFile, IOListener ioListener) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(pathFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
return;
}
readLine2String(fileInputStream, ioListener);
}
/**
* 一行一行地读
*
* @param inputStream
* @param ioListener
*/
public void readLine2String(InputStream inputStream, IOListener ioListener) {
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
try {
inputStreamReader = new InputStreamReader(inputStream, encodeType);
bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
long current = 0;
String str;
while (isRunning && (str = bufferedReader.readLine()) != null) {
sb.append(str);
current += str.length();
ioListener.onLoading(str, current, contentLength);
}
//中断
if ((str = bufferedReader.readLine()) != null) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted(sb.toString());
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
close(bufferedReader);
close(inputStreamReader);
close(inputStream);
}
}
public void readL2StrNoBuffer(String pathFile, IOListener ioListener) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(pathFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
return;
}
readL2StrNoBuffer(fileInputStream, ioListener);
}
/**
* 一行一行地读,不拼接
*
* @param inputStream
* @param ioListener
*/
public void readL2StrNoBuffer(InputStream inputStream, IOListener ioListener) {
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
try {
inputStreamReader = new InputStreamReader(inputStream, encodeType);
bufferedReader = new BufferedReader(inputStreamReader);
long current = 0;
String str;
while (isRunning && (str = bufferedReader.readLine()) != null) {
current += str.length();
ioListener.onLoading(str, current, contentLength);
}
//中断
if ((str = bufferedReader.readLine()) != null) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted("");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
close(bufferedReader);
close(inputStreamReader);
close(inputStream);
}
}
public void readL_N2String(String pathFile, IOListener ioListener) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(pathFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
return;
}
readL_N2String(fileInputStream, ioListener);
}
/**
* 一行一行地读,\n拼接
*
* @param inputStream
* @param ioListener
*/
public void readL_N2String(InputStream inputStream, IOListener ioListener) {
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
try {
inputStreamReader = new InputStreamReader(inputStream, encodeType);
bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
long current = 0;
String str;
while (isRunning && (str = bufferedReader.readLine()) != null) {
sb.append(str);
sb.append("\n");
current += str.length();
ioListener.onLoading(str, current, contentLength);
}
//中断
if ((str = bufferedReader.readLine()) != null) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted(sb.toString());
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
close(bufferedReader);
close(inputStreamReader);
close(inputStream);
}
}
/**
* 读取到文件
*
* @param inputStream
* @param outputStream
* @param ioListener
*/
public void read2File(InputStream inputStream, OutputStream outputStream, IOListener ioListener) {
try {
byte[] buffer = new byte[1024];
int len = 0;
long current = 0;
while (isRunning && (len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
current += len;
ioListener.onLoading(new String(buffer), current, contentLength);
}
outputStream.flush();
//中断
if (len != -1) {
ioListener.onInterrupted();
} else {
ioListener.onCompleted(null);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(outputStream);
close(inputStream);
}
}
/**
* 将str写入文件
*/
public void writeStr2File(String str, String pathFile, IOListener ioListener) {
BufferedWriter bufferedWriter = null;
OutputStreamWriter outputStreamWriter = null;
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(pathFile);
outputStreamWriter = new OutputStreamWriter(outputStream);
bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write(str);
ioListener.onCompleted("");
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
close(bufferedWriter);
close(outputStreamWriter);
close(outputStream);
}
}
/**
* 将str写入文件,同步操作,独占锁
*/
public void writeStr2ReplaceFileSync(String str, String pathFile, IOListener ioListener) {
File file;
try {
file = FileUtils.createFile(pathFile);
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail("文件创建失败,请检查路径是否合法以及读写权限");
return;
}
FileOutputStream fileOutputStream = null;
FileChannel fileChannel = null;
FileLock fileLock = null;//文件锁
try {
/**
* 写文件
*/
fileOutputStream = new FileOutputStream(file);
fileChannel = fileOutputStream.getChannel();
while (true) {
try {
fileLock = fileChannel.tryLock();//独占锁
break;
} catch (Exception e) {
System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
}
}
if (fileLock != null) {
int len = 0;
long current = file.length();
if (isRunning ) {
fileChannel.write(ByteBuffer.wrap(str.getBytes()));
current += len;
LogUtils.log("当前线程" + Thread.currentThread().getName());
ioListener.onLoading(str.getBytes(), current, str.length());
}else {
//中断
ioListener.onInterrupted();
}
if (fileLock != null && fileLock.isValid()) {
LogUtils.log("release-write-lock");
fileLock.release();
}
close(fileChannel);
close(fileOutputStream);
if (file.length() == str.getBytes().length) {
ioListener.onCompleted(file);
}
}
} catch (IOException e) {
e.printStackTrace();
ioListener.onFail(e.getMessage());
} finally {
}
}
}
写文件使用示例
new IOUtils().writeStr2ReplaceFileSync(jsonObjectOld.toJSONString(), Constants.PATH_GAME_JSON, new IOListener() {
@Override
public void onCompleted(Object result) {
}
@Override
public void onLoading(Object readedPart, long current, long length) {
}
@Override
public void onInterrupted() {
}
@Override
public void onFail(String errorMsg) {
}
});
读文件使用示例
new IOUtils().read2StrSync(Constants.PATH_CONFIG_APPLICATION_JSON, new IOListener<String>() {
@Override
public void onCompleted(String result) {
}
@Override
public void onLoading(String readedPart, long current, long length) {
}
@Override
public void onInterrupted() {
}
@Override
public void onFail(String errorMsg) {
}
});
各位老铁有问题欢迎及时联系、指正、批评、撕逼
Github:
https://github.com/AnJiaoDe
简书:
https://www.jianshu.com/u/b8159d455c69
微信公众号
这里写图片描述
QQ群
这里写图片描述
网友评论