这种方式不会加大内存占用,经过测试4g内存的文件内存占用一直没有很大。
事实上,常用的
FileInputStream
,
FileOutputStream
都是通过
FileChannel
进行读写的,而FileChannel可以设置开始读、写的位置,demo如下:
public static void task(String fileName,int batchCnt) throws Exception{
long start = System.currentTimeMillis();
File toWrite = new File("src\\"+fileName + ".pdf");
File toRead = new File("C:\\Users\\86134\\Desktop\\book\\实现领域驱动设计.pdf");
CountDownLatch cdl = new CountDownLatch(batchCnt);
long size = toRead.length();
long patch = size/batchCnt;
for (int i = 0; i < batchCnt; i++) {
int finalI = i;
GlobalThreadPool.execute(()-> {
long end = patch * (finalI+1);
try {
write(toWrite,toRead,patch* finalI,end);
} catch (Exception e) {
e.printStackTrace();
}finally {
cdl.countDown();
cdl.await();
System.out.println(batchCnt+"线程任务耗时"+(System.currentTimeMillis()-start)+"ms");
public static void write(File toWrite,File toRead,long start,long end) throws Exception{
FileInputStream fis = new FileInputStream(toRead);
FileOutputStream fos = new FileOutputStream(toWrite);
fis.getChannel().position(start);
fos.getChannel().position(start);
fos.getChannel().force(true);
byte[] tmp = new byte[batchCnt];
int s;
while (true) {
int curBatch = (int)Math.min(end-start,batchCnt);
s = fis.read(tmp,0, curBatch);
start += s;
if(s<=0 || start>end) break;
fos.write(tmp, 0, curBatch);
fis.close();
fos.close();
经过测试,文件可以正常打开。
测试代码如下:
public static void main(String[] args) throws Exception {
with1Thread();
with2Thread();
with3Thread();
with4Thread();
with5Thread();
with6Thread();
1线程任务耗时1297ms
2线程任务耗时705ms
3线程任务耗时659ms
4线程任务耗时468ms
5线程任务耗时508ms
6线程任务耗时399ms
考虑到开线程的损耗,可以说确实起到了需要的效果。
这里为什么不用JMH?
我的考虑是不太清楚具体的原理(相关是如何执行的?),而且每次都需要重新生成一个文件,为了确保条件尽可能类似,需要任务执行完成之后进行删除。因此在书写了对应的JMH测试之后,发现效率特别慢。
但如果将文件变大,多线程反而就没有这种倍增的效果。
mmap设置大小
这次将文件变成了一个大小为4g的来做一下对比。
java中,mmap最大只能映射Integer.MAX_VALUE(2^31-1)的内存块,需要设置起始位置.
mmap实际上也是FileChannel映射的,需要指定start(从哪里开始映射)和size(从start开始映射多少长度)
public static void job() throws IOException, InterruptedException {
long start = System.currentTimeMillis();
File w = new File("C:\\Users\\86134\\Desktop\\book\\"+"mmap" + ".7z");
while(!w.exists() && !w.createNewFile());
File r = new File("C:\\Users\\86134\\Desktop\\book\\src.7z");
Path toWrite = w.toPath();
Path toRead = r.toPath();
long total = r.length();
long cnt = total/Integer.MAX_VALUE;
if(cnt*Integer.MAX_VALUE>total) cnt++;
CountDownLatch cdl = new CountDownLatch((int)cnt);
for (long i = 0; i <= cnt; i++) {
long end = Math.min(total,(i+1)*Integer.MAX_VALUE);
long finalI = i;
GlobalThreadPool.execute(()->{
try {
part(toWrite,toRead, finalI *Integer.MAX_VALUE,end);
cdl.countDown();
} catch (IOException e) {
e.printStackTrace();
cdl.await();
System.out.println("mmap多线程耗时:"+(System.currentTimeMillis()-start)+"ms");
System.exit(0);
public static void part(Path toWrite,Path toRead,long start,long end) throws IOException {
FileChannel readChannel = FileChannel.open((toRead), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open((toWrite), StandardOpenOption.WRITE);
MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY,start,end-start);
writeChannel.position(start);
writeChannel.write(data);
readChannel.close();
writeChannel.close();
同时也将上面FileChannel中,将文件换成了相同的,做了类似的对比,结果如下:
mmap2线程耗时:20922ms
FileChannel2线程任务耗时34565ms
在单纯的速度上mmap占优,但是在内存上,MMAP有一个较大的问题。
测试平台是win10,fileChannel的方式并不会多吃掉多少内存,然而mmap会大概多吃掉2~3g的内存,这个地方需要去研究研究。
- 126
-
BiaDisaForTruth
LeetCode
- 1544
-
BiaDisaForTruth
Spring