Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中共享变量的顺序使用_Java_Multithreading_Swing_Shared Memory_Event Dispatch Thread - Fatal编程技术网

Java中共享变量的顺序使用

Java中共享变量的顺序使用,java,multithreading,swing,shared-memory,event-dispatch-thread,Java,Multithreading,Swing,Shared Memory,Event Dispatch Thread,我正在开发一个JavaGUI应用程序。在某个事件中(例如,单击按钮),我希望从新工作线程中的文件中读取大量数据,并将其存储到共享变量中。然后从这个工作线程调用EDT(事件调度线程)并使用数据 没有数据的并发访问-只需在一个线程中创建数据,完成后,在另一个线程中使用它 我不确定从工作线程调用EDT是否确保EDT将看到更新的数据,或者EDT是否可能只与一些旧的现金版本一起工作 我不想使用易失性字段和同步的集合,因为我的数据类的结构很复杂,而且我不想使用不必要的同步来降低代码的速度 伪代码示例: ED

我正在开发一个JavaGUI应用程序。在某个事件中(例如,单击按钮),我希望从新工作线程中的文件中读取大量数据,并将其存储到共享变量中。然后从这个工作线程调用EDT(事件调度线程)并使用数据

没有数据的并发访问-只需在一个线程中创建数据,完成后,在另一个线程中使用它

我不确定从工作线程调用EDT是否确保EDT将看到更新的数据,或者EDT是否可能只与一些旧的现金版本一起工作

我不想使用易失性字段和同步的集合,因为我的数据类的结构很复杂,而且我不想使用不必要的同步来降低代码的速度

伪代码示例:

EDT: MyType data;

EDT: start new Worker thread;
Worker: loadDataFromFileInNewThread(data);
Worker: invokeEDT; //using java.awt.EventQueue.invokeLater
EDT: use data;

这种共享内存的使用是否正常,或者我是否必须强制EDT以某种方式使用更新版本?

在工作线程上,调用
java.awt.EventQueue.invokeLater
最终锁定
java.awt.EventQueue.pushPopLock
,然后再将您的Runnable排入队列。这意味着您的工作人员此时已在该锁上同步

在EDT方面,EDT在一个循环中调用
java.awt.EventQueue.getNextEvent
,以接收和处理工作
getNextEvent
locks
java.awt.EventQueue.pushPopLock
在退出Runnable的队列之前

此时,我们知道您的工作线程和EDT都在同一个对象上进行了同步,根据JMM,这在两个线程之间创建了一个内存刷新点

因此,在您的特定场景中,您不需要额外的同步或
volatile
,以确保EDT能够看到由工作线程创建的最新信息——只要您的工作线程在调用
invokeLater
后停止修改共享数据(这无论如何都是最佳实践)


实现细节:旧版本的Java不使用
Java.util.concurrent.locks.Lock
,而是在EventQueue的方法上使用
synchronized
方法标志。但是,与上述相同的规则适用于锁。

最简单的方法是为异步加载任务使用
SwingWorker
,因为它简化了与EDT的同步,并提供了一些挂钩,用于定期向EDT发送更新(例如,用于进度报告):

//只能由EDT访问
MyData MyData;
类MyDataLoder扩展SwingWorker{
@凌驾
公共MyData doInBackground(){
//此方法在后台线程中运行
//在这里加载数据吗
//定期更新进度:setProgress(…);
返回加载的数据;
}
@凌驾
受保护的void done(){
//在doInBackground完成后调用
//在EDT中运行
试一试{
//将数据发布到EDT
myData=get();
}捕获(异常忽略){}
}
}
如您所见,数据通过
done()
方法移交给EDT。此方法在EDT中调用,在那里,数据通过
get()
从后台线程安全地“拉”到EDT,因此它被安全地发布到EDT


作为一个很好的副作用,您可以将一些进度条绑定到
SwingWorker
progress
属性,这样您就可以显示加载进度。

遗憾的是,这没有正确的文档记录。我希望它能正常工作。但可以肯定的是,您确实可以简单地更新一个可变变量或原子引用。读取可变变量不会对应用程序的性能产生任何显著影响。阅读一个大文件:就像登上月球然后回来。读取原子引用:就像眨眼睛一样。您可以使用future和executer在future对象中获取数据,这将使您能够知道数据何时在工作线程中完全读取,因此您可以在itFYI中选择任何您想要的内容,invokeLater不会启动EDT。EDT已在运行,正在等待事件。一定是这样,因为您的程序已经处理了一次按钮单击。当你想让EDT做一些事情时,你可以发布一个事件。invokeLater()方法发布了一个通用的、与GUI无关的事件,基本上意味着要这样做……james——我基本上同意。迂腐点:如果到那时为止他的程序中没有执行任何与AWT相关的操作,invokeLater将启动EDT。正如您所注意到的,如果他的工作人员是响应AWT按钮点击而启动的,那么显然AWT-EDT已经在工作人员调用invokeLater.james的时候启动了,当然,只是用词不当,不管怎么说,我只是想知道:这是否适用于workerThread1->workerThread2关系,还是仅适用于worker->EDT?我考虑过SwingWorker,但这是不可行的-我需要任务可以重复,并且创建新SwingWorker的时间限制为10分钟,加上完成的SwingWorker只会挂起10分钟,直到处理完毕(常规线程在大多数情况下似乎是一个更好的选择)@Abdul我理解。从来没有听说过10分钟的问题。但我认为你仍然可以接受主要想法——让一个后台线程来完成工作——最好通过
ExecutorService
——并通过EDT发布结果。所以你必须自己做
调用器(此::完成)
-没什么大不了的。
// should only be accessed by the EDT
MyData myData;

class MyDataLoder extends SwingWorker<MyData, Object> {
    @Override
    public MyData doInBackground() {
        // this method runs in a background thread
        // do loading of data here
        // update progress periodically: setProgress(...);

        return loadedData;
    }

    @Override
    protected void done() {
        // called after doInBackground completes
        // runs in EDT
        try {
            // publish your data to the EDT
            myData = get();
        } catch (Exception ignore) { }
    }
}