一、概述
java下的多線程其實就是對RandomAccessFile類的應用,事實上就是一個很純粹、很基礎的java程序。RandomAccessFile最變態的是它可以對文件進行讀寫。java的多線程,其實就是學習RandomAccessFile這個類。
二、RandomAccessFile類
此類的實例支持對隨機訪問文件的讀取和寫入。隨機訪問文件的行爲類似存儲在文件系統中的一個大型 byte 數組。存在指向該隱含數組的光標或索引,稱爲文件指針;輸入操作從文件指針開始讀取字節,並隨着對字節的讀取而前移此文件指針。如果隨機訪問文件以讀取/寫入模式創建,則輸出操作也可用;輸出操作從文件指針開始寫入字節,並隨着對字節的寫入而前移此文件指針。寫入隱含數組的當前末尾之後的輸出操作導致該數組擴展。該文件指針可以通過 getFilePointer 方法讀取,並通過 seek 方法設置。
多線程應用的方法和構造方法有:
* RandomAccessFile(File file, String mode) 關注mode的取值
“r” 以只讀方式打開。調用結果對象的任何 write 方法都將導致拋出 IOException。
“rw” 打開以便讀取和寫入。如果該文件尚不存在,則嘗試創建該文件。
“rws” 打開以便讀取和寫入,對於 “rw”,還要求對文件的內容或元數據的每個更新都同步寫入到底層存儲設備。
“rwd” 打開以便讀取和寫入,對於 “rw”,還要求對文件內容的每個更新都同步寫入到底層存儲設備。
write(byte[] b, int off, int len) 不多說
seek(long pos) 設置到此文件開頭測量到的文件指針偏移量,在該位置發生下一個讀取或寫入操作。
三、多線程步驟簡述
- 與服務器連接前,先設置對一個線程中文件分段下載情況:
conn.setRequestProperty("Range" , "bytes="+startIndex +"-"+endIndex);
- 然後用RandomAccessFile的對象進行寫入,
RandomAccessFile raf = new RandomAccessFile("/mnt/sdcard/wubi.exe","rw");
- 跳過n個字符(其它線程下載)
raf.seek(startIndex);
- 開時寫入
raf.write(buf, 0, len);
四、測試程序
package com.sky.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 這個類模擬三個線程同時下載,但是不能斷點續傳
*
* */
public class ThreeThreadDownLoad {
private static final int THREAD_COUNT = 3;
private static int runningThread;
public static void main(String[] args) throws Exception {
String path = "http://localhost:8080/wubi.exe";
//下面是文件讀取開始和結束的標記
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int length = conn.getContentLength();
//根據服務器的文件大小,在本地也創建一個空的大小 的文件。
RandomAccessFile raf = new RandomAccessFile("wubi.exe", "rw");
raf.setLength(length);
//每一個線程文字讀取開始和結束的標記
int startIndex;
int endIndex;
runningThread = THREAD_COUNT;
//
//下面是開三個線程
for (int threadId = 0; threadId<THREAD_COUNT; threadId++) {
startIndex=threadId*length/THREAD_COUNT;
endIndex=(threadId+1)*length/THREAD_COUNT-1;
if(threadId==THREAD_COUNT-1){
//如果是最後一個線程,考慮不能整除的情況
endIndex=length-1;
}
//每次開啓三個線程,並把字節的首和尾傳進去
new MyThread(startIndex,endIndex,threadId,path).start();
}
}
static class MyThread extends Thread{
private int startIndex;
private int endIndex;
private int threadId;
private String path;
private int currentPosition;
public MyThread(int startIndex, int endIndex, int threadId, String path) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
this.path = path;
currentPosition = startIndex;
}
@Override
public void run() {
try {
//這裏是判斷之前是否下載過,下載過就接着懟
File pf = new File(threadId+".position");
if(pf.exists()){
FileInputStream fos = new FileInputStream(threadId+".position");
BufferedReader br = new BufferedReader(new InputStreamReader(fos));
String line = br.readLine();
currentPosition=Integer.valueOf(line);
System.out.println("這個文件被線程:"+threadId+"下載過,"+"下載的最後的位置是:"+currentPosition);
}
//1.與服務器建立連接,並得到連接對象
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range" , "bytes="+startIndex +"-"+endIndex);
//2.連接對象得到文件的相關信息
//下面這句話已經對服務器請求了,如果setRequestProperty方法一定要放在連接請求的前面。
int code = conn.getResponseCode();
//3.建立文件讀寫類,開懟
RandomAccessFile raf = new RandomAccessFile("wubi.exe","rw");
System.out.println("…………………………");
raf.seek(startIndex);
//告訴服務器,客戶端想要從什麼位置型讀取,讀到什麼時候結束
//3.文件斷點下載時,狀態碼是206,不是200
if(code==206){
//4.得到輸入流,
InputStream is = conn.getInputStream();
byte[] buf = new byte[1024];
int len=0;
//6.讀出並寫入
while((len=is.read(buf))!=-1){
raf.write(buf, 0, len);
currentPosition+=len;
//下面是斷點續傳的文件記錄
RandomAccessFile frec=new RandomAccessFile(threadId+".position", "rws");
frec.write((currentPosition+"").getBytes());
frec.close();
}
//7.關流
is.close();
raf.close();
System.out.println("下載完成");
synchronized (ThreeThreadDownLoad.class) {
runningThread--;
if(runningThread<=0){
for (int i = 0; i < THREAD_COUNT; i++) {
File positionfile = new File(i+".position");
positionfile.delete();
}
}
}
}
System.out.println("Thread:"+threadId+"下載完成,下載了"+(endIndex-startIndex));
} catch ( Exception e) {
e.printStackTrace();
System.out.println("下載失敗");
}
}
}
}
五、正式的程序
try {
//這裏是判斷之前是否下載過,下載過就接着懟
File pf = new File(threadId+".position");
if(pf.exists()){
FileInputStream fos = new FileInputStream(threadId+".position");
BufferedReader br = new BufferedReader(new InputStreamReader(fos));
String line = br.readLine();
currentPosition=Integer.vralueOf(line);
System.out.println("這個文件被線程:"+threadId+"下載過,"+"下載的最後的位置是:"+currentPosition);
}
//1.與服務器建立連接,並得到連接對象
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range" , "bytes="+startIndex +"-"+endIndex);
//2.連接對象得到文件的相關信息
int code = conn.getResponseCode();
//3.建立文件讀寫類,開懟
RandomAccessFile raf = new RandomAccessFile("/mnt/sdcard/wubi.exe","rw");
System.out.println("…………………………");
raf.seek(startIndex);
//告訴服務器,客戶端想要從什麼位置型讀取,讀到什麼時候結束
//3.文件斷點下載時,狀態碼是206,不是200
if(code==206){
//4.得到輸入流,
InputStream is = conn.getInputStream();
byte[] buf = new byte[1024];
int len=0;
//6.讀出並寫入
while((len=is.read(buf))!=-1){//這裏一定要注意把read放到buf裏
raf.write(buf, 0, len);
currentPosition+=len;
//下面是斷點續傳的文件記錄
RandomAccessFile frec=new RandomAccessFile("/mnt/sdcard/"+threadId+".position", "rws");
frec.write((currentPosition+"").getBytes());
frec.close();
//下面是對進度條的更新
if(threadId==0){
pb1.setMax(endIndex-startIndex);
pb1.setProgress(currentPosition-startIndex);
}else if(threadId==1){
pb2.setMax(endIndex-startIndex);
pb2.setProgress(currentPosition-startIndex);
}else if(threadId==2){
pb3.setMax(endIndex-startIndex);
pb3.setProgress(currentPosition-startIndex);
}
}
//7.關流
is.close();
raf.close();
System.out.println("下載完成");
synchronized (MyThread.class) {
runningThread--;
if(runningThread<=0){
for (int i = 0; i < THREAD_COUNT; i++) {
File positionfile = new File("/mnt/sdcard/"+i+".position");
positionfile.delete();
}
}
}
}
System.out.println("Thread:"+threadId+"下載完成,下載了"+(endIndex-startIndex));
} catch ( Exception e) {
e.printStackTrace();
System.out.println("下載失敗");
}