【问题标题】:Java Concurrent ExceptionJava 并发异常
【发布时间】:2015-04-29 23:52:24
【问题描述】:

您好,我有一个正在绘制图像的paint 方法,我还有另一种不断修改要绘制的图像的方法,但是我时不时地遇到并发异常。请问解决这个问题的最有效方法是什么?我知道我可以在缓冲图像上使用同步块,但它会在同步非最终变量时引发警告。

private BufferedImage img;

public void modImage(BufferedImage image) { 
     img = image;
}

public void paintComponent(Graphics g) {
    if (img != null) {
        g.drawImage(img, 0, 0, this);
    }
}

【问题讨论】:

  • 您可以在两个不同的缓冲区之间交替。一个用于渲染,一个用于编辑。
  • 抱歉,您能否举个例子说明如何使用缓冲区实现这一目标
  • 在非最终变量上同步本质上没有错。您可能将警告级别设置得过高。
  • @immibis:如果是synchronized (img),那就有很多问题了,这就是“在缓冲图像上使用同步块”的意思。
  • @user2357112 当然,存在涉及synchronized 和非最终变量的潜在错误,但 IMO 该警告的信噪比太低了。 (为什么不是“在非最终变量上调用方法”警告?)

标签: java multithreading concurrency locks


【解决方案1】:

您可以在实例上同步,

private BufferedImage img;

public void modImage(BufferedImage image) { 
     synchronized(this){
         img = image;
     }
}

public void paintComponent(Graphics g) {
    synchronized(this){
    if (img != null) {
        g.drawImage(img, 0, 0, this);
       }
    }
}

【讨论】:

    【解决方案2】:

    您有一个竞争条件,因为img 的值可以在另一个线程中在if 条件和drawImage 中使用img 之间更改

    if (img != null) {                     // <-- img can be non-null here
        g.drawImage(img, 0, 0, this);      // <-- img can now be null
    }
    

    您可以将img 分配给局部变量,然后在上面的代码中使用局部变量代替img。即使 img 在另一个线程中更改为不同的对象,局部变量也将保持不变

    final BufferedImage localImg = img;      // <-- localImg won't change locally
    if (localImg != null) {
        g.drawImage(localImg, 0, 0, this);
    }
    

    除此之外,img 应声明为volatile,因此其值不会在线程本地缓存;一个线程中的更改将被其他人看到。

    private volatile BufferedImage img;
    

    请记住,将变量声明为volatile 将导致在访问该变量时发生同步;所以同步仍在中。但是,同步发生在 img 引用本身上,而不是它所引用的 BufferedImage 对象上,因此如果 imgnull,则这里没有问题。

    【讨论】:

    • 没有同步,不能保证读取img的线程会看到其他线程的写入。
    • @user2357112 好点! img 需要被声明为 volatile。我会更新我的答案。
    【解决方案3】:

    存在线程安全问题,因为BufferedImage 的内容可以在paintComponent 方法运行时更改。仅 ui 类中的同步和 volatile 无法解决此问题。

    如果要添加同步,则需要确保对图像的任何修改也同步。这需要在涉及的每个类之间共享的某个对象上同步。

    您可以通过确保将img 设置为BufferedImage 的副本或每次都设置不同的实例来避免一些同步。不过它应该仍然是易变的,如果你想安全地将img 设置回null,请检查 pathfinderelite 的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多