通过使用Java的ExecutorService来产生竞争状况吗?

时间:2018-09-19 14:03:39

标签: java http concurrency

我有一个可以轻松实现HTTP Ranged-GET协议的类。本质上,一些代码调用该类,告诉它下载文件。下面的MyClass负责以偶数大小的块(直到最后一个块,它可以是可变长度)下载文件,并将其发送到另一个服务。调用时,它将根据来自第一个HTTP响应的Content-Range实例长度来设置文件大小。

该类使用线程池大小为1的ExecutorService来控制线程。

下面是相关的实现,其中一些函数处理了GET和PUT。

class MyClass implements Runnable {
    private long start;
    private long chunkSize;
    private int chunkNumber;
    private int fileSize = 0;

    private static final int MAX_RETRIES = 3;

    public static final ExecutorService ES = Executors.newSingleThreadExecutor();

    public MyClass(long start, long chunkSize, int chunkNumber) {
        this.start = start;
        this.chunkSize = chunkSize;
        this.chunkNumber = chunkNumber;
    }

    public void run() {
        for (int i = 0; i < MAX_RETRIES; i++) {

            long end = start + chunkSize - 1; // inclusive so subtract 1

            // doHttpGet() is a private instance function...
            // if fileSize == 0 (i.e. first chunk downloaded), this will set the fileSize
            doHttpGet(start, end);

            // doHttpPost() is a private instance function
            // it builds the POST from the GET message, which I'm not bothering to show here
            if (!doHttpPost()) {
                continue;
            } else {
                submitNextChunk(this);
                break;
            }
        }
    }

    // this is the function a client uses to invoke the class
    public static void submitWork(long newStartByte, long chunkSize, int chunkNumber) {
        MyClass mc = new MyClass(newStartByte, chunkSize, chunkNumber);
        if (ES.submit(mc) == null) {
            //log error
        }
    }

    // PROBLEM AREA?!?!
    private static void submitNextChunk(MyClass mc) {
        mc.chunkNumber++;
        mc.start += mc.chunkSize;
        // LOGGER.debug("start=" + mc.start + "\n" + "fileSize=" + mc.fileSize)
        if (mc.start < mc.fileSize) {
            if (ES.submit(mc) == null) {
                //log error
            }
        }
    }
}

这是调用MyClass的代码片段。

long chunkSize = //somecalculation
DownloadAction.submitWork(0L, chunkSize, 1));

此代码可以正常运行很长时间了。但是,我现在注意到当要下载的文件非常小时(例如,<50个字节),这可能是不确定的行为。似乎正在发生的事情是submitNextChunk()函数似乎无法正确评估mc.start < mc.fileSize。例如,如果我们设置packetSize = 100K并使用一个50字节的文件,那么我通过Wireshark看到的是连续HTTP GET请求,请求字节0-99999、100000-199000和200000-299000,。 ..等。(另一端的代码也稍作中断,因为它继续为我们提供了最初的50个字节,而不是HTTP超出范围的错误代码...但这是另一回事了。)

我担心的是比赛条件微妙:

如果我在submitNextChunk()中添加一些日志记录以打印出startfileSize,那么我 only 只能看到一个日志语句,其中start = 100000和fileSize = 100,并且该函数将小于表达式正确评估为false。这是有道理的,因为这将是第一次,也是唯一一次调用submitNextChunk(),并且由于该表达式的计算结果为false,因此该函数将中止。

我担心这是一个线程问题,因为没有调试语句,小于表达式显然会导致true,这本不应该发生。

0 个答案:

没有答案