简体   繁体   English

在线程之间共享对象的最佳方法?

[英]Best way to share object between threads?

I have a main Swing app with class members BufferedImage lastCapturedImage , ScheduledExecutorService executor with 2 threads in the thread pool. 我有一个带有类成员BufferedImage lastCapturedImageScheduledExecutorService executor程序的主Swing应用程序,线程池中有2个线程。

BufferedImage lastCapturedImage;
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
...
executor.scheduleWithFixedDelay(imageCaptureRunnable, 100, 1000 / TARGET_FPS, TimeUnit.MILLISECONDS);
executor.schedule(roboticArmRunnable, 500, TimeUnit.MILLISECONDS);

The first Runnable pulls images ( BufferedImage ) from a webcam and updates a class instance lastCapturedImage . 第一个Runnable从网络摄像头提取图像( BufferedImage )并更新类实例lastCapturedImage

private final Runnable imageCaptureRunnable = new Runnable() {

  @Override
  public void run() {
    lastCapturedImage = webcam.getImage();
  }

};

The second Runnable processes the image and controls a robotic arm. 第二个Runnable处理图像并控制机械臂。 The image capturing rate is much faster than the robotic arm consumer and only the most current image is needed by the robotic arm consumer. 图像捕获速率比机械手消耗器快得多,并且机械手消耗器仅需要最新的图像。 How best do I share the image in a thread safe way? 如何最好地以线程安全的方式共享映像?

After researching this topic, my solution is to wrap the image ( lastCapturedImage ) in a synchronized block in the roboticArmRunnable 's run() method, and make a copy of the image like this: 在研究了这个主题之后,我的解决方案是将图像( lastCapturedImage )包装在roboticArmRunnablerun()方法中的synchronized块中,并像这样复制图像:

private final Runnable roboticArmRunnable = new Runnable() {

  @Override
  public void run() {
    while(true){
      BufferedImage clonedCameraCapture;
      synchronized (lastCapturedImage) {
        clonedCameraCapture = copyImage(lastCapturedImage);
      }
      // process the clonedCameraCapture image and move the robotic arm
    }
  }
};

My understanding is that the synchronized block will allow for the roboticArmRunnable to completely make the copy of the image before the imageCaptureRunnable is allowed to update the image. 我的理解是,在允许imageCaptureRunnable更新图像之前, synchronized块将允许roboticArmRunnable完全复制图像。 Am I doing this right?? 我这样做正确吗?

Thanks in advance! 提前致谢!

No, your code isn't safe. 不,您的代码不安全。

First of all, for synchronization to be correct, all the access to the sharedstate must be synchronized, and not just the read access. 首先,为了使同步正确,必须对共享状态的所有访问进行同步,而不仅仅是读取访问。

Second: synchronizing on a non-final field is wrong: since the field can change, one thread will acquire the lock on the old value and the second thread will then be able to enter the same synchronized section because the field has changed. 第二:在非最终字段上进行同步是错误的:由于该字段可以更改,因此一个线程将获得对旧值的锁定,而第二个线程将可以输入相同的同步段,因为该字段已更改。

You don't have any atomicity problem to solve here: writing and reading a reference is guaranteed to be atomic. 您这里没有要解决的原子性问题:编写和阅读参考文献肯定是原子性的。 You have a visibility problem to solve though: nothing guarantees that the write made by the image reader thread (that writes the reference) will be visible by the robotic arm thread (that reads the reference). 但是,您需要解决一个可见性问题:没有任何东西可以保证图像读取器线程(写入引用)所做的写入将被机械臂线程(读取引用)可见。

So all you need is to make the field volatile, or to wrap it inside an AtomicReference: 因此,您需要做的是使字段可变,或将其包装在AtomicReference中:

private volatile BufferedImage lastImage;

or 要么

private AtomicReference<BufferedImage> lastImageRef;

...
// in image reader
lastImageRef.set(theNewImage);

...
// in robotic arm
BufferedImage lastImage = lastImageRef.get();

If you were still willing to solve the visibility problem using synchronization, you would have to do something like: 如果您仍然愿意使用同步来解决可见性问题,则必须执行以下操作:

static class LastImageHolder
    private BufferedImage lastImage;

    public synchronized BufferedImage get() {
        return lastImage;
    }

    public synchronized BufferedImage set(BufferedImage lastImage) {
        this.lastImage = lastImage;
    }
}

private LastImageHolder lastImageHolder = new LastImageHolder();

Functionality like this can be done without locks. 这样的功能无需锁即可完成。 Here's a OneOf : 这是一个OneOf

class OneOf<T> {

    volatile int which = 0;
    final T[] them;

    public OneOf(T[] them) {
        this.them = them;
    }

    public T get() {
        return get(0);
    }

    public T get(int skip) {
        return them[(which + skip) % them.length];
    }

    public void skip() {
        which += 1;
        which %= them.length;
    }
}

You can now use get() to get the current one, get(1) to peek at the following one. 现在,您可以使用get()获取当前的内容,而get(1)可以查看下一个内容。 In your case your image reader will choose it's image using get(1) to begin filling the next image while your robot will read the image using get() to get the current one. 在您的情况下,图像读取器将使用get(1)选择其图像以开始填充下一幅图像,而您的机器人将使用get()读取该图像以获取当前图像。

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

相关问题 Java:在不同线程之间共享锁的最佳方法 - Java: best way to share Lock between different threads 在线程之间传递信息的最佳方式是什么? - What is the best way to pass information between threads? 在线程之间同步 object 的最佳实践 - Best practice for synchronizing an object between threads 使用JBoss和Spring在Java Web应用程序之间共享业务对象实例的最佳方法是什么? - What's the best way to share business object instances between Java web apps using JBoss and Spring? Java Jersey:什么是在主对象和Web服务之间共享数据的最佳方法 - Java Jersey: Whats the best way to share data between the Main-object and the Webservice 如何在两个线程之间共享对象(线程同步)? - How to Share a Object between two threads (Thread Synchronization)? 如何在线程之间共享对象以证明它不是线程安全的? - How to share an object between threads to demonstrate that it is not thread-safe? 在不同线程中的类之间传递对象的最佳方法? - The best way to pass objects between classes in different threads? 处理文档队列时在线程之间分配工作的最佳方法 - The best way to divide work between threads when processing a queue of documents 在具体的JPA类之间共享Java实现的最佳方法? - Best way to share Java implementation between concrete JPA classes?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM