繁体   English   中英

多个 Java 线程中的变量同步

[英]Variable synchronization in multiple Java Threads


我的问题是关于如何使用块同步,我的类FileAdapter有一个方法write接收我用来下载文件的 HTTP 连接结果的InputStream ,对于下载并写入磁盘的每千字节,它调用接收到的DownloadReport类实例的下载方法,用于传递已经下载的内容。

在另一个向用户打印输出的Thread中,它调用也是DownloadReport类的updateProgress方法,该方法负责更新在终端上显示给用户的进度条。

问题将是,如果FileAdapter类尝试更新在输出线程尝试更新进度条时下载的字节数,因为这两种方法都编辑变量intermediateDownloaded的值,该变量仅用作辅助变量,以保存自上次更新以来下载的字节数,以计算下载速度。

如果我使用“已同步(this)”块,在下载updateProgress方法中,它将阻止整个类DownloadReport ,并且输出Thread将只能在FileAdapter类更新下载字节数后更新进度条?

文件适配器:

public void write(InputStream content, DownloadReport downloadReport) throws IOException {

    FileOutputStream output = new FileOutputStream(file);

    byte[] buffer = new byte[1024];

    int read;

    while ((read = content.read(buffer)) != -1) {
        output.write(buffer, 0, read);
        downloadReport.downloaded(read);
    }
}

下载报告:

public void downloaded(int bytes) {
    intermediateDownloaded += bytes;
    downloaded += bytes;
}

public void updateProgress() {

    long now = System.currentTimeMillis();
    double delta = UnitHelper.sizeRound(((now - lastTimeUpdate) / 1000.0), 2);

    if (delta >= 1) {
        unitAdapter.convertSpeed(intermediateDownloaded, delta);

        intermediateDownloaded = 0;
        lastTimeUpdate = now;
    }

    progressBar.updateProgress(unitAdapter.finalSize,
            unitAdapter.recalculate(downloaded), unitAdapter.unity);
}

首先,这种类型的同步在最近十年或更长时间已经失宠,因为:

  • 做对是非常困难的
  • 它在性能方面受到影响(由于等待)
  • 这是无法测试的。

使用这种方法,您的代码中可能总是存在错误,可能导致竞态条件,而您不会知道,并且没有可以编写的单元测试来确保您的代码不受竞态条件的影响。 线程之间通信的现代方法是使用消息队列传递不可变消息。

现在,如果您坚持这样做,那么synchronize( this )是个坏主意,因为持有对您对象的引用的人也可以执行synchronized( yourObject ) ,然后您就会陷入死锁。 synchronized方法也是如此,因为编译器在后台使用synchronized( this )来实现它们。 所以,也不要那样做。 始终声明一个私有对象用作锁。

此外,正如您似乎已经理解的那样,同步锁需要尽可能少地处于活动状态,以避免阻塞可能也需要获取该锁的其他线程。 因此,它需要包装尽可能少的指令。

对于您的代码,如果我理解正确,您需要执行以下操作:

private final Object lock = new Object();

public void downloaded( int bytes )
{
    synchronized( lock )
    {
        downloaded += bytes;
    }
}

然后再往下走,每当您访问时,您还必须与lock同步进行,并且您需要找到其他方法来计算downloaded downloaded奇怪的intermediateDownloaded变量,以便它不需要参与同步.

或者,您可以用下载的java.util.concurrent.atomic.AtomicLong downloaded替换long downloaded ,这将允许您读取它并比使用同步更高效地更新它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM