- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试创建一个 java 程序,将某些 Assets 文件从 FTP 服务器下载到本地文件。由于我的(免费)FTP 服务器不支持超过几兆字节的文件大小,因此我决定在上传文件时将其拆分,并在程序下载文件时重新组合它们。这可行,但速度相当慢,因为对于每个文件,它都必须获取InputStream
,这需要一些时间。
我使用的 FTP 服务器有一种无需实际登录服务器即可下载文件的方法,因此我使用此代码来获取 InputStream
:
private static final InputStream getInputStream(String file) throws IOException {
return new URL("http://site.website.com/path/" + file).openStream();
}
要获取 Assets 文件一部分的InputStream
,我使用以下代码:
public static InputStream getAssetInputStream(String asset, int num) throws IOException, FTPException {
try {
return getInputStream("assets/" + asset + "_" + num + ".raf");
} catch (Exception e) {
// error handling
}
}
由于 getAssetInputStreams(String, int)
方法需要一些时间来运行(特别是文件大小超过一兆字节时),我决定将实际下载文件的代码编写为多字节线程。这就是我的问题所在。
final Map<Integer, Boolean> done = new HashMap<Integer, Boolean>();
final Map<Integer, byte[]> parts = new HashMap<Integer, byte[]>();
for (int i = 0; i < numParts; i++) {
final int part = i;
done.put(part, false);
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream is = FTP.getAssetInputStream(asset, part);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[DOWNLOAD_BUFFER_SIZE];
int len = 0;
while ((len = is.read(buf)) > 0) {
baos.write(buf, 0, len);
curDownload.addAndGet(len);
totAssets.addAndGet(len);
}
parts.put(part, baos.toByteArray());
done.put(part, true);
} catch (IOException e) {
// error handling
} catch (FTPException e) {
// error handling
}
}
}, "Download-" + asset + "-" + i).start();
}
while (done.values().contains(false)) {
try {
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
File assetFile = new File(dir, "assets/" + asset + ".raf");
assetFile.createNewFile();
FileOutputStream fos = new FileOutputStream(assetFile);
for (int i = 0; i < numParts; i++) {
fos.write(parts.get(i));
}
fos.close();
此代码有效,但并不总是有效。当我在台式计算机上运行它时,它几乎总是有效。不是 100% 的时候,但通常效果很好。在我的笔记本电脑上,它的互联网连接要差得多,它几乎无法工作。结果是文件不完整。有时,它会下载 50% 的文件。有时,它会下载 90% 的文件,每次都不同。
现在,如果我将 .start()
替换为 .run()
,则代码 100% 正常工作,即使在我的笔记本电脑上也是如此。然而,它的速度非常慢,所以我宁愿不使用 .run()
。
有没有办法可以更改我的代码,使其能够多线程工作?任何帮助将不胜感激。
最佳答案
首先,更换您的 FTP 服务器,有很多免费的 FTP 服务器支持任意文件大小,并具有附加功能,但我离题了...
您的代码似乎存在许多不相关的问题,这些问题都可能导致您所看到的行为,如下所述:
通过多个线程的不 protected /不同步访问来访问 done
和 parts
映射会产生竞争条件。这可能会导致数据损坏以及线程之间这些变量失去同步,从而可能导致 done.values().contains(false)
返回 true
,即使实际上并非如此。
您频繁地重复调用 done.values().contains()
。虽然 javadoc 没有明确说明,但 HashMap 可能会以 O(n) 方式遍历每个值,以检查给定映射是否包含某个值。再加上其他线程正在修改映射,您将得到未定义的行为。根据 values()
javadoc:
If the map is modified while an iteration over the collection is in progress (except through the iterator's own remove operation), the results of the iteration are undefined.
您以某种方式调用new URL("http://site.website.com/path/"+ file).openStream();
,但声明您正在使用 FTP。链接中的 http://
定义了 openStream()
尝试打开的协议(protocol),并且 http://
不是 ftp ://
。不确定这是一个拼写错误还是您指的是 HTTP(或者您是否有提供相同文件的 HTTP 服务器)。
任何线程引发任何类型的异常
都会导致代码失败,因为并非所有部分都会“完成”(基于您的忙等待循环设计)。当然,您可能会编辑一些其他逻辑来防止这种情况,但否则这是代码的潜在问题。
您没有关闭任何已打开的流。这可能意味着底层套接字本身也保持打开状态。这不仅会造成资源泄漏,如果服务器本身有某种最大同时连接数限制,则只会导致新连接失败,因为旧的已完成的传输不会关闭。
基于上述问题,我建议将下载逻辑移至 Callable 任务中,并通过 ExecutorService
运行它们,如下所示:
LinkedList<Callable<byte[]>> tasksToExecute = new LinkedList<>();
// Populate tasks to run
for(int i = 0; i < numParts; i++){
final int part = i;
// Lambda to
tasksToExecute.add(() -> {
InputStream is = null;
try{
is = FTP.getAssetInputStream(asset, part);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[DOWNLOAD_BUFFER_SIZE];
int len = 0;
while((len = is.read(buf)) > 0){
baos.write(buf, 0, len);
curDownload.addAndGet(len);
totAssets.addAndGet(len);
}
return baos.toByteArray();
}catch(IOException e){
// handle exception
}catch(FTPException e){
// handle exception
}finally{
if(is != null){
try{
is.close();
}catch(IOException ignored){}
}
}
return null;
});
}
// Retrieve an ExecutorService instance, note the use of work stealing pool is Java 8 only
// This can be substituted for newFixedThreadPool(nThreads) for Java < 8 as well for tight control over number of simultaneous links
ExecutorService executor = Executors.newWorkStealingPool(4);
// Tells the executor to execute all the tasks and give us the results
List<Future<byte[]>> resultFutures = executor.invokeAll(tasksToExecute);
// Populates the file
File assetFile = new File(dir, "assets/" + asset + ".raf");
assetFile.createNewFile();
try(FileOutputStream fos = new FileOutputStream(assetFile)){
// Iterate through the futures, writing them to file in order
for(Future<byte[]> result : resultFutures){
byte[] partData = result.get();
if(partData == null){
// exception occured during downloading this part, handle appropriately
}else{
fos.write(partData);
}
}
}catch(IOException ex(){
// handle exception
}
使用执行器服务,您可以进一步优化多线程场景,因为只要片段(按顺序)可用,输出文件就会开始写入,并且线程本身会被重用,以节省线程创建成本。
如上所述,可能存在太多同时链接导致服务器拒绝连接的情况(或者更危险的是,编写 EOF 让您认为该部分已下载)。在这种情况下,可以通过 newFixedThreadPool(nThreads)
调整工作线程的数量,以确保在任何给定时间,只能同时发生 nThreads
数量的下载。
关于java - 多线程 FTP 输入流的输出不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30683475/
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: Recreating a Dictionary from an IEnumerable 在 Dictiona
是否可以使用命令行版本的 ImageMagick 修剪图像(比如带有 alpha 的 PNG),使输出图像的宽度和高度都是偶数(不是奇数)? 准确地说,应该先修剪输出图像,然后用透明像素填充。我需要这
我有一个订单的Map,可以由许多不同的线程访问。我想控制访问,所以考虑以下简单的数据结构+包装器。 public interface OrderContainer { boolean cont
我有以下代码,现在只是 div 中的一个 Logo ,但我正在尝试添加一些导航单元格,稍后我将对其进行样式设置。问题是,我似乎无法让它们与(除此之外) Logo “一致”,它们总是下降到下一行。我做错
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
有没有办法将种子值传递给 d3-cloud 或其他基于 javascript 的标签云,以使其在页面加载之间保持一致? 我们的客户希望使用标签云作为导航/发现辅助工具,但由于 d3-cloud 会在每
我有一条由用户使用 D3.js 绘制的路径。 我想在我的用户绘制路径上定义一个破折号数组,但是,随着它改变其形状和长度,破折号的行为不一致并且间隙在移动并变得越来越小。 这是一个代码笔: https:
只是为了研究UINavigationBar和UIStatusBar的UI,我把Navigation Bar Style改成了Black,并且取消勾选Bar visibility,即Shows Navi
我最近在我的家用机器 (OSX 10.9) 和我的远程服务器 (Ubuntu 12.04 64 位) 上安装了 unison。 我在这两个地方都安装了 2.40.102 版本。我在我的 Mac 上使用
我正在使用 migrate 创建 SQL 数据库模式并用初始数据填充它。后来使用 SQLAlchemy 来处理这个数据库。 我如何测试我的 SQLAlchemy 模型是否与 migrate 生成的真实
道歉对这一切来说还是新鲜事。我正在创建一个网页,并在两个单独的 div 中将图像和文本并排放置。我已经设法将它们放在页面上我想要的位置,但是当我调整页面大小时,文本会调整大小,但图像不会。我希望文本底
在翻阅Cassandra和HBase的阅读资料时,我发现Cassandra并不一致,但HBase是一致的。没有找到任何合适的阅读 Material 。 有人可以提供有关此主题的任何博客/文章吗? 最佳
我需要计算 MacOS 中文件夹的大小。该尺寸值必须与 Finder 一致。我尝试了几种方法来做到这一点。但结果总是与Finder不同。 以下方法是我尝试过的。 typedef struct{
问:我可以使用 C++ 中的任何编译时机制来自动验证模板类方法集是否从类特化到特化相匹配? 示例:假设我想要一个类接口(interface),它根据模板值专门化具有非常不同的行为: // forwar
我想使用 SelectKBest 选择前 K 个特征并运行 GaussianNB: selection = SelectKBest(mutual_info_classif, k=300) data_t
我想要一个位于页面中央的 div,其中包含一行(两个单词)的 h1 文本,并且该文本与 div 的长度对齐;意思是,字母留出空间(同时保持它们的大小)以占据 div 的整个宽度,并且不要超出 div。
我试图更新我的服务器,所以我通过 ssh 运行以下命令: sudo do-release-upgrade 我收到以下错误: Errors were encountered while processi
我想验证单应矩阵会给出好的结果,而这个 this answer 有答案 - 但是,我不知道如何实现答案。 那么谁能推荐我如何使用 OpenCV 计算 SVD 并验证第一个奇异值与最后一个奇异值的比率是
我最近更新到 cocoapods 0.36 并对内部规范做了一些更改,现在 podspec 不再有效。我用 0.35 验证了此规范的先前版本 (0.3.8),但使用 0.36 失败。很明显 cocoa
我有两个并排设置的 TableView ,我需要它们同时滚动。因此,当您滚动一个时,另一个也会同时滚动。 我进行了一些搜索,但找不到任何信息,但我认为这一定是有可能的。 我的 TableView 都连
我是一名优秀的程序员,十分优秀!