因此,我尝试使用线程池和ConcurrentLinkedQueue来进行大量下载。我的问题是,在下面的(可运行的)代码中,当Threads开始时,各个SingleDownloads的日历日期的设置随机切换。我之所以感到困惑,是因为ConcurrentLinkedQueue是线程安全的。我必须在某处犯错。
SAMPLE OUTPUT:
app30 2014-12-24 2015-01-23
app29 2014-12-26 2015-01-24
app28 2014-12-28 2015-01-25
...
OUTPUT OMITTED
...
Downloading: app29 2014-12-26 2015-01-24
Downloading: app28 2014-12-30 2015-01-26
Downloading: app30 2014-12-26 2015-01-24
如上所示,日期会随机变化。
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.concurrent.ConcurrentLinkedQueue;
public class DownStack {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
private volatile static boolean running = true;
private volatile static int threadsCompleted;
private static ConcurrentLinkedQueue<Runnable> taskQueue;
private static DownloadThread[] downloadThreads;
private static String[] status;
static class SingleDownload {
String app;
Calendar from;
Calendar to;
File folder;
public SingleDownload(String app, Calendar from, Calendar to,
File folder) {
this.app = app;
this.from = from;
this.to = to;
this.folder = folder;
}
}
static class DownloadTask implements Runnable {
private SingleDownload param;
private int index;
public DownloadTask(SingleDownload param, int index) {
this.param = param;
this.index = index;
}
public void run() {
status[index] = "downloading";
downloadOne(param.app, param.from, param.to, param.folder);
status[index] = "finished";
}
}
private static class DownloadThread extends Thread {
public void run() {
try {
while (running) {
Runnable task = taskQueue.poll();
if (task == null)
break;
task.run();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadFinished();
}
}
}
synchronized private static void threadFinished() {
threadsCompleted++;
if (threadsCompleted == downloadThreads.length) {
running = false;
downloadThreads = null;
}
}
public static void downloadOne(String app, Calendar from, Calendar to,
File folder) {
System.out.println("Downloading: " + app + " "
+ sdf.format(from.getTime()) + " " + sdf.format(to.getTime()));
}
public static void main(String[] args) {
File folder = new File(".");
ArrayList<SingleDownload> downloadList = new ArrayList<>();
for (int i = 30; i > 0; i--) {
Calendar cal1 = Calendar.getInstance();
cal1.add(Calendar.DAY_OF_YEAR, -2 * i);
Calendar cal2 = Calendar.getInstance();
cal2.add(Calendar.DAY_OF_YEAR, -1 * i);
downloadList.add(new SingleDownload("app" + i, cal1, cal2, folder));
}
for (SingleDownload sd : downloadList) {
System.out.println(sd.app + " " + sdf.format(sd.from.getTime())
+ " " + sdf.format(sd.to.getTime()));
}
status = new String[downloadList.size()];
int index = 0;
taskQueue = new ConcurrentLinkedQueue<Runnable>();
for (SingleDownload fd : downloadList) {
Runnable r = new DownloadTask(fd, index);
taskQueue.add(r);
index++;
}
int threadCount = 20;
downloadThreads = new DownloadThread[threadCount];
running = true;
threadsCompleted = 0;
for (int i = 0; i < threadCount; i++) {
downloadThreads[i] = new DownloadThread();
try {
downloadThreads[i].setPriority(Thread.currentThread()
.getPriority() - 1);
} catch (Exception e) {
}
downloadThreads[i].start();
}
while (running) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// downloadTable.repaint();
}
}
}
答案 0 :(得分:5)
我认为问题是由于您使用SimpleDateFormat
- 它不是线程安全的。如果您每次需要使用它时都创建一个新的SimpleDateFormat
,那么您的结果应该是稳定的。 (订单不会,但没关系。)
如果可能,我强烈建议您使用{8}的Joda Time或java.time
包,而不是Date
/ Calendar
/ SimpleDateFormat
- 他们是更好的API,SimpleDateFormat
的等价物是线程安全的。