junit多线程有坑!!!

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的动作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值