junit多线程测试有坑~
今天在测试自己写的一个多线程下载器的时候,在最后用junit单元测试的时候一直得不到想要的结果,程序进行到一半就突然中止了。
下面先贴出下载器的代码
package com.coderising.multiThreadDownloader;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class MultiThreadDownloader {
private String downloadAdress = "http://img.xmpig.com/forum/201610/20/201816z4zvp3zov8vtpp5g.jpg";
private String storePathString = "f:/";
private int threadNum = 4;
public MultiThreadDownloader() {
}
public MultiThreadDownloader(String downloadAdress, String storePathString, int threadNum) {
this.downloadAdress = downloadAdress;
this.storePathString = storePathString;
this.threadNum = threadNum;
}
public void download() {
try {
URL url = new URL(downloadAdress);
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setReadTimeout(10000);
int code=httpURLConnection.getResponseCode();
if (code== 200) {
int len = httpURLConnection.getContentLength();
RandomAccessFile rFile = new RandomAccessFile(new File(storePathString, DownloadThread.getFileName(url)), "rw");
rFile.setLength(len);
int threadSize = len / threadNum;
for (int id = 0; id < threadNum; id++) {
int startIndex = id * threadSize;
int endIndex = (id + 1) * threadSize - 1;
if (id == threadNum - 1) {
endIndex = len - 1;
}
Thread thread = new DownloadThread(id, downloadAdress,
storePathString, startIndex, endIndex);
thread.start();
}
//Thread.sleep(3000);
System.out.println("download success");
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
上面代码是主要根据地址获得资源的大小,把资源划分为不同的块儿,分别启用下面的线程,下载各自的范围块儿
package com.coderising.multiThreadDownloader;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class DownloadThread extends Thread {
private int id;
private String downloadPath;
private String storePath;
private int startIndex;
private int endIndex;
public DownloadThread(int id, String downloadPath,String storePath, int startIndex, int endIndex) {
this.downloadPath = downloadPath;
this.id = id;
this.storePath=storePath;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
System.out.println("Thread " + id + "开始下载:");
URL url = new URL(downloadPath);
File tmpFile = new File(storePath, getFileName(url) + id);
RandomAccessFile rFile = null;
if (tmpFile.exists()) {
rFile = new RandomAccessFile(tmpFile, "rwd");
String start = rFile.readLine();
this.startIndex = Integer.parseInt(start);
} else {
rFile = new RandomAccessFile(tmpFile, "rwd");
}
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(10000);
httpURLConnection.setRequestProperty("Range", "bytes="+ startIndex + "-" + endIndex);
int code=httpURLConnection.getResponseCode();
System.out.println("Thread "+id+" code: "+code);
if ( code== 206) {//206表示部分资源获得成功
InputStream inputStream = httpURLConnection.getInputStream();
RandomAccessFile randomAccessFile = new RandomAccessFile(new File(storePath, getFileName(url)), "rw");
randomAccessFile.seek(startIndex);
byte[] tmp = new byte[1024];
int length = -1;
int total = 0;
while ((length = inputStream.read(tmp)) > 0) {
randomAccessFile.write(tmp, 0, length);
total += length;
rFile.seek(0);
rFile.write((startIndex + total + "").getBytes("UTF-8"));
}
rFile.close();
inputStream.close();
randomAccessFile.close();
cleanTmp(tmpFile);
System.out.println("thread "+id+"下载完成");
}else{
System.out.println("响应吗:"+code+"--不支持多线程下载");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileName(URL url) {
String name = url.getFile();
name = name.substring(name.lastIndexOf("/") + 1);
return name;
}
private synchronized void cleanTmp(File file){
file.delete();
}
}
下载器的逻辑并不复杂,junit测试就是new一个MultiThreadDownloader最后期望的对象,然后执行download()方法,结果就是在"F:/"下面得到所下载的图片,并且能够正确的显示,控制台应该显示:
Thread 0开始下载:
Thread 1开始下载:
Thread 3开始下载:
Thread 2开始下载:
Thread 0 code: 206
Thread 3 code: 206
Thread 1 code: 206
Thread 2 code: 206
thread 2下载完成
thread 0下载完成
thread 3下载完成
thread 1下载完成
download success
但是最后结果却显示
Thread 0开始下载:
download success
Thread 1开始下载:
Thread 2开始下载:
Thread 3开始下载:
图片当然也没有下载完成。我把自己的代码看了又看,小bug倒是有一些,但是并没有找到程序终止的原因,而且程序每次终止的结果都不一样。
最后想到是不是junit不太会玩儿,哪儿出错了,因此在MultiThreadDownloader类里面,用一个main方法进行测试,最后居然下载成功了。所以果然还是junit的锅。
最后经过查询,junit并不适合多线程,在junit的主线程运行完毕以后,会强行停止测试,并不会考虑到创建的子线程是否执行完毕,所以出现了上面程序莫名停止在不同的地方。
所以,如果你依旧想用junit测试多线程,需要额外的操作,来保证子线程的完成:
1.用Thread.sleep()强行停止主线程,等待子线程执行完毕,缺点就是等待多长时间,需要你主观去把握。
2.使用CountDownLatch类,每个子线程计数器减1,直到计数器减为0,然后结束主线程。
3.使用CyckicBarrier类,线程集等待预定数目到达一个barrier时,选择执行一个处理barrier的动作。