package io;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* RandomAccessFile 類讀寫測試及其性能優化(一)
*
* GenerateIntArray生成count個數組,每個數組裏面有size個整數,然後將生成的數據寫入文件.
* 整數通過Random.nextInt(total)來生成.
* 初始化時,數組數據都是0,調用refreshDataArr()可生成新數據填充數組
*
* 生成數據的方法有:
* (1) refreshDataArr() 使用雙重循環生成數據.
* (2) refreshDataArr_M() 使用count個線程,每個線程中生成size個數據
*
* 將數據寫入文件的方法有:
* (1)writeData2File(File f)
* 循環調用RandomAccessFile.writeInt(int)方法,每個數據都寫一次,一共寫了count*size次
* (2)writeData2File_B(File f)
* 該方法先將coutn個整數轉換成字符數組,並將count個字節數組按順序組合到一個大的字節數組中
* 然後調用RandomAccessFile.write(byte[] byteArr);方法一次性寫入size個整數.
* (3)writeData2File_M(File f)
* 該方法啓動count個線程,每個線程使用writeData2File_B中的方法,一次性寫入size個整數
*
* 由下面的測試結果可知,通過RandomAccessFile.write(byte[] byteArr)寫入字節數組的方式一次性寫入
* size個整數時寫入速度最快,比一次寫入一個整數快了很多.多線程寫入時性能提升不大,只有在count不大,但是
* size巨大時多線程方法寫入有一些提升,因爲生成count個線程並且要進行線程調度也需要消耗一些系統資源.
*
* 多線程方式生成數據,也只有在size特別大(100000),count不是很大時有速度提升.
*
* RandomAccessFile進行多線程寫似乎並不能提升速度,其中原因有待研究.
*
* 數據量非常小時,使用單線程一次生成一個數據,以及一次寫入一個整數時速度快
* (因爲使用寫字節數組方式一次性寫入size個整數時需要將整數轉換成字節數組,這有一定的開銷).
*
* 下面是部分測試數據(耗時單位是 耗時(納秒)/100000)
*
*
count = 10, size = 10
正在生成數據,請稍後...
refreshDataArr 生成數據成功, 耗時:0
正在生成數據,請稍後...
refreshDataArr_M 生成數據成功, 耗時:96
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_B寫入數據耗時:16
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File 寫入數據耗時:12
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_M寫入數據耗時:39
----------------------------------------------------------------
count = 100, size = 1000
正在生成數據,請稍後...
refreshDataArr 生成數據成功, 耗時:123
正在生成數據,請稍後...
refreshDataArr_M 生成數據成功, 耗時:303
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_B寫入數據耗時:246
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File 寫入數據耗時:6623
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_M寫入數據耗時:308
----------------------------------------------
count = 100, size = 10000
正在生成數據,請稍後...
refreshDataArr 生成數據成功, 耗時:894
正在生成數據,請稍後...
refreshDataArr_M 生成數據成功, 耗時:1645
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_B寫入數據耗時:753
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File 寫入數據耗時:70863
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_M寫入數據耗時:1605
------------------------------------
count = 1000, size = 10000
正在生成數據,請稍後...
refreshDataArr 生成數據成功, 耗時:9078
正在生成數據,請稍後...
refreshDataArr_M 生成數據成功, 耗時:15067
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_B寫入數據耗時:25753
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File 寫入數據耗時:701698
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_M寫入數據耗時:13727
----------------------------------------------
count = 1000, size = 100000
正在生成數據,請稍後...
refreshDataArr 生成數據成功, 耗時:86459
正在生成數據,請稍後...
refreshDataArr_M 生成數據成功, 耗時:82820
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_B寫入數據耗時:345980
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File 寫入數據耗時:0(耗時太長,這裏放棄測試)
正在寫入數據,請稍後...
數據已寫入文件D:\D\test_data.dat\test_data.dat
writeData2File_M寫入數據耗時:273550
*
*/
public class GenerateIntArray
{
private int count = 1000; // 數組的個數,
private int size = 10; // 每個數組的元素個數
private int[][] dataArr;
private Random random = new Random(1000);
public GenerateIntArray()
{
dataArr = new int[count][size];
}
public GenerateIntArray(int count, int size)
{
this.count = count;
this.size = size;
this.dataArr = new int[count][size];
}
public int[][] getDataArr()
{
return dataArr;
}
/**
* 刷新數組中的數據
*/
public int[][] refreshDataArr()
{
int total = count * size;
for (int i = 0; i < count; i++)
{
for (int j = 0; j < size; j++)
{
dataArr[i][j] = random.nextInt(total);
}
}
return dataArr;
}
private class getIntTask implements Runnable
{
private int arrIndex;
private CountDownLatch latch;
public getIntTask(int arrIndex,CountDownLatch latch)
{
this.arrIndex = arrIndex;
this.latch = latch;
}
@Override
public void run()
{
int total = count * size;
for(int i = 0;i < size;i++)
{
dataArr[arrIndex][i] = random.nextInt(total);
}
latch.countDown();
}
}
/*
* 多線程方式生成數組中的數據
* 啓動count個線程,每個線程中產生size個整數
*/
public int[][] refreshDataArr_M()
{
CountDownLatch latch = new CountDownLatch(count);
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<count;i++)
{
exec.execute(new getIntTask(i,latch));
}
try
{
latch.await();
//保證在所有生成數據線程沒有完成之前,當前方法不返回.
}
catch (InterruptedException e)
{
e.printStackTrace();
}
exec.shutdown();
return dataArr;
}
/**
* 寫數組數據到文件,如果文件已經存在,則會被刪除,然後重新生成文件
* 每次寫入數組中的一個數據
* @param f
* @throws IOException
*/
public void writeData2File(File f) throws IOException
{
if (null != f && f.exists())
{
f.delete();
}
RandomAccessFile rf = new RandomAccessFile(f, "rw");
rf.seek(0);// 每次都從頭開始些文件
for (int i = 0; i < count; i++)
{
for (int j = 0; j < size; j++)
{
rf.writeInt(dataArr[i][j]);
}
}
rf.close();
}
/**
* 寫數據時,現將整數轉換成字節數據保存,然後一次性寫入字節數組到文件,
* 避免頻繁寫入.
* @param f
* @throws IOException
*/
public void writeData2File_B(File f) throws IOException
{
if (null != f && f.exists())
{
f.delete();
}
RandomAccessFile rf = new RandomAccessFile(f, "rw");
rf.seek(0);// 每次都從頭開始些文件
for (int i = 0; i < count; i++)
{
byte[] byteArr = new byte[4 * size];
int iTmp = 0;
for (int j = 0; j < size; j++)
{
byte[] tmpBytes = int2byte(dataArr[i][j]);
byteArr[iTmp++] = tmpBytes[3];
byteArr[iTmp++] = tmpBytes[2];
byteArr[iTmp++] = tmpBytes[1];
byteArr[iTmp++] = tmpBytes[0];
}
rf.write(byteArr);
}
rf.close();
}
/**
* 多線程方式同時同時寫文件
* @param f
* @throws IOException
*/
class WriteTask implements Runnable
{
private File f;
private int dataIndex;
public WriteTask(File f,int dataIndex)
{
this.f = f;
this.dataIndex = dataIndex;
}
@Override
public void run()
{
try
{
RandomAccessFile rf = new RandomAccessFile(f, "rw");
rf.skipBytes(dataIndex * size * 4 );
byte[] byteArr = new byte[4 * size];
int iTmp = 0;
for (int j = 0; j < size; j++)
{
byte[] tmpBytes = int2byte(dataArr[dataIndex][j]);
byteArr[iTmp++] = tmpBytes[3];
byteArr[iTmp++] = tmpBytes[2];
byteArr[iTmp++] = tmpBytes[1];
byteArr[iTmp++] = tmpBytes[0];
}
rf.write(byteArr);
rf.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public void writeData2File_M(File f) throws IOException
{
if (null != f && f.exists())
{
f.delete();
}
//先生成一個固定尺寸的文件,能夠保存所有整數的
RandomAccessFile rf = new RandomAccessFile(f, "rw");
rf.setLength(count * size * 4 ); //設置尺寸(一個整型佔4字節)
rf.seek(0);
//rf.write(1024);//隨便寫一個,以便保存文件
rf.close();
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<count;i++)
{
exec.execute(new WriteTask(f,i));
}
exec.shutdown();
}
// 將二進制數轉換成字節數組
private byte[] int2byte(int res)
{
byte[] targets = new byte[4];
targets[0] = (byte) (res & 0xff);// 最低位
targets[1] = (byte) ((res >> 8) & 0xff);// 次低位
targets[2] = (byte) ((res >> 16) & 0xff);// 次高位
targets[3] = (byte) (res >>> 24);// 最高位,無符號右移
return targets;
}
public static void main(String[] args)
{
int count = 10;
int size = 1000;
boolean bPrintData = false; //是否打印生成的數組,當數據量大是不打印,只在小數據量時打印以便測試
System.out.printf("count = %d, size = %d \n\n",count,size);
GenerateIntArray generator = new GenerateIntArray(count, size);
File f;
try
{
f = new File("D:\\D\\test_data.dat");
System.out.println("正在生成數據,請稍後...");
long startTmie = System.nanoTime();
generator.refreshDataArr();
long totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("refreshDataArr 生成數據成功, 耗時:" + totalTime);
System.out.println();
System.out.println("正在生成數據,請稍後...");
startTmie = System.nanoTime();
generator.refreshDataArr_M();
totalTime = (System.nanoTime() - startTmie)/ 100000;;
System.out.println("refreshDataArr_M 生成數據成功, 耗時:" + totalTime);
System.out.println();
System.out.println("正在寫入數據,請稍後...");
startTmie = System.nanoTime();
generator.writeData2File_B(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;;
System.out.println("數據已寫入文件" + f.getPath() + File.separator + f.getName());
System.out.println("writeData2File_B寫入數據耗時:" + totalTime);
System.out.println();
System.out.println("正在寫入數據,請稍後...");
startTmie = System.nanoTime();
generator.writeData2File(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("數據已寫入文件" + f.getPath() + File.separator + f.getName());
System.out.println("writeData2File 寫入數據耗時:" + totalTime);
System.out.println();
System.out.println("正在寫入數據,請稍後...");
startTmie = System.nanoTime();
generator.writeData2File_M(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;;
System.out.println("數據已寫入文件" + f.getPath() + File.separator + f.getName());
System.out.println("writeData2File_M寫入數據耗時:" + totalTime);
System.out.println();
if(bPrintData)
{
System.out.println("原始數組中生成的數據...");
int[][] intArr = generator.getDataArr();
for (int i = 0; i < count; i++)
{
for (int j = 0; j < size; j++)
{
System.out.printf("%d ", intArr[i][j]);
}
System.out.println();
}
System.out.println("從文件中讀取出來的數據...");
RandomAccessFile rf = new RandomAccessFile(f, "r");
rf.seek(0);
int iline = 1;
while (true)
{
System.out.printf("%d ",rf.readInt());
if(iline % size == 0)
{
System.out.println();
}
iline ++;
// 判斷已經到文件尾了
if (rf.getFilePointer() >= rf.length() - 1)
{
break;
}
}
rf.close();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}