Java ConcurrentLinkedQueue的奇迹

时间:2015-02-22 16:01:09

标签: java concurrency reference

因此,我尝试使用线程池和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();
        }

    }
}

1 个答案:

答案 0 :(得分:5)

我认为问题是由于您使用SimpleDateFormat - 它不是线程安全的。如果您每次需要使用它时都创建一个新的SimpleDateFormat,那么您的结果应该是稳定的。 (订单不会,但没关系。)

如果可能,我强烈建议您使用{8}的Joda Timejava.time包,而不是Date / Calendar / SimpleDateFormat - 他们是更好的API,SimpleDateFormat的等价物是线程安全的。