【问题标题】:Thread safe two-way communucation between main thread and a single long-running thread主线程和单个长时间运行线程之间的线程安全双向通信
【发布时间】:2019-09-27 15:42:49
【问题描述】:

我有一个应用程序,它有一个类/进程,它使用基于第三方 C 的本机库进行一些繁重的工作。这个库有一些它自己的依赖项和数据,它在第一次运行时会加载到内存中(接近 500MB)。加载需要几秒钟,所以它只需要发生一次。这个控制类还构建了一个大型的对象哈希图,这些对象是从与本机库一起使用的数据库中提取的。

我的问题是这个。控制类需要更新并在外部引用大型哈希图。例如:当外部有更新时,hashmap 中的相关对象需要更新其内容,当主线程需要使用 hashmap 中的特定对象时,它需要访问它或它的副本。我一直在说“主线程”,但实际情况是应用程序的主要部分使用来自另一个多线程本机库的事件侦听器。并且新事件很快出现(在每秒 100 到 200 个的范围内,并且在生产中可能会更高)。

我需要的是一种来回传递数据的线程安全方式。我的第一个想法是使用单例,这适用于少量请求(每秒 15 个),但是一旦增加,我就会遇到导致 JVM 崩溃的线程问题。我的下一个想法是使用阻塞队列,但据我所知,没有办法可靠地进行双向通信。

让我解释一下最后一条语句(我知道出队)。必须发生的事情是,当一个进程对控制类说“我需要对象 74”时,它需要能够返回对象 74。如果两个单独的进程请求一个对象(比如 74 而另一个请求 89),则使用出队没有办法保证每个过程都会得到正确的。至少要等到它收到对象并检查它是哪一个之后。

接下来我考虑了应用程序本身的事件侦听器模式。基本上,主逻辑类(发送更新和请求副本的逻辑类)会将自身的引用传递给控制器​​类,并且控制器类将实现 @Override 方法。我什至不确定这是否是个好主意,但我什至没走多远就意识到它也不是线程安全的。

所以我一直在努力思考可以在这里工作的设计模式。通常我可以解决这些问题,但最近家人去世让我盯着我的屏幕没有解决办法。我知道这是一个已经解决了一百万次的古老问题,但我现在无法完成这个过程。

感谢任何帮助。

【问题讨论】:

  • 第一段不清楚C 库内部发生了什么以及Java 应用程序内部发生了什么。我建议编辑澄清。此外,您应该描述在 C 和 Java 之间进行通信的技术手段。
  • 感谢您的回复。 C 库的作用无关紧要。我试图解决的所有问题都在 Java 本身中。 Java 部分是业务逻辑,需要是线程安全的。与 C 库的通信按预期工作。所以这没有问题。事实上,在描述我的问题时,没有必要提及 C 库。
  • 如果我理解正确,您需要通过某个索引同时随机访问对象。那么为什么ConcurrentHashMap 不起作用呢?

标签: java multithreading thread-safety


【解决方案1】:

感谢 St.Antario(在上面的 cmets 中),我找到了解决方案。

我尝试的第一件事是 ConcurrentHashMap,但由于 3rd 方库的非线程安全性质,我第二次猜到了自己。

最终,解决方案是隔离原生库并在自己的线程中运行它。我通过 BlockingQueue 将数据传递给本机库。所以在我的代码的其他地方,我只使用 myQueue.put(data) 调用来传递数据。这现在运作良好。

本机库线程代码如下所示:

public void Start() {
    Runnable task = () -> {
        try {
            /* SETUP NATIVE LIBRARY */
        } catch (Exception e) {
            e.printStackTrace();
        }
        while (keepRunning) {
            while (!myQueue.isEmpty()) {
                Process_Run();
            }
            try {
                Thread.sleep(20);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        myThread = new Thread(task); // Run the task in a background thread
        myThread.setDaemon(true); // Terminate the running thread if the application exits
        myThread.start(); // Start the thread
    }

private void Process_Run() {
    try {
        MyCustomData myData = myQueue.take();
        /*  DO STUFF  */
    } catch (Exception e) {
        e.printStackTrace();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多