需要明确的是,我不相信这种方法实际上会提高下载速度。但是,如果您从多个镜像下载相同的文件,它可能会提供更一致的下载速度。
首先,如果您的文件不是太大,您可以在写出之前缓冲所有文件。所以分配一个所有线程都可以访问的缓冲区:
byte[] buf = new byte[fileSize];
现在您创建一个合适的线程类型:
public class WriterThread extends Thread
{
byte[] buf;
int write_pos, write_remaining;
public WriterThread(byte[] buf, int start, int len)
{
this.buf = buf;
this.write_pos = start;
this.write_remaining = len;
}
@Override
public void run()
{
try (Socket s = yourMethodForSettingUpTheSocketConnection();
InputStream istream = s.getInputStream()) {
while (this.write_remaining > 0) {
int read = istream.read(this.buf, this.write_pos, this.write_remaining);
if (read == -1) error("Not enough data received");
this.write_remaining -= read;
this.write_pos += read;
}
// otherwise you are done reading your chunk!
}
}
}
WriterThread
现在,您可以使用合适的起点和长度来启动尽可能多的这些对象。例如,对于大小为 6000 字节的文件:
byte[] buf = new byte[6000];
WriterThread t0 = new WriterThread(buf, 0, 3000);
WriterThread t1 = new WriterThread(buf, 3000, 3000);
t0.start();
t1.start();
t0.join();
t1.join();
// check for errors
请注意这里的重要一点:每个 WriterThreads 都有一个对完全相同的缓冲区的引用,只是它开始写入的偏移量不同。当然,您必须确保yourMethodForSettingUpTheSocketConnection
请求数据从 offset 开始this.write_pos
;您如何做到这一点取决于您使用的网络协议,并且超出了您的要求。
如果您的文件太大而无法放入内存,则此方法将不起作用。相反,您必须使用(较慢的)方法,首先创建一个大文件,然后写入该文件。虽然我没有尝试过,但您应该可以使用java.nio.file.File.newByteChannel()' to set up a suitable
SeekableByteChannel as your output file. If you create such a
SeekableByteChannel sbc`,然后您应该可以这样做
sbc.location(fileSize - 1); // move to the specified position in the file
sbc.write(java.nio.ByteBuffer.allocate(1)); // grow the file to the expected final size
然后SeekableByteChannel
每个线程使用一个不同的对象,指向磁盘上的同一个文件,并使用该SeekableByteChannel.location(int)
方法设置写入开始位置。你需要一个临时byte[]
的,你可以围绕它包装一个ByteBuffer
(via ByteBuffer.wrap()
),但除此之外,该策略类似于上面的:
thread_local_sbc.location(this.write_pos);
然后 eachthread_local_sbc.write()
将写入从 . 开始的文件this.write_pos
。