首页 > 编程知识 正文

JavaFX 中使用多线程与保证 UI 线程安全,android如何保证线程安全

时间:2023-05-06 12:15:25 阅读:201331 作者:804

JavaFX 中使用多线程与保证 UI 线程安全 JavaFX 中使用多线程JavaFX 中保证 UI 线程安全总结与补充

  JavaFX 中的 UI 线程和大多数其它的编程语言一样,是单线程的。前人很早就已经多次尝试在 UI 线程上使用多线程,大多都已失败告终。为保证 UI 界面的流畅,UI 线程不能执行非常耗时的操作。如果 UI 线程执行正在非常耗时的操作,这个后果在 UI 界面的体现就是,UI 界面会一直停滞在执行耗时代码前的状态,然后如果马上随意连续点击 UI 界面的任何部位,此时会发生如下现象:

应用的标题会加上 (未响应) 的后缀。

应用的关闭按钮会变红。

光标位于此应用中时,光标会变成加载的圆圈图样。

操作系统会将此应用的界面变成灰色,然后弹窗提示此程序已停止响应。

  上面的就是俗称应用卡死的状态。通过上面的描述应该可以明白,不是说一旦进入卡死的状态,就只能手动强制结束这个应用。应用卡死的状态只能一种 UI 界面被阻塞的状态(UI 界面无法自主更新)。当发生了这个状态,并不能说明程序就发生了死锁,因此此时如果等待,程序就有可能自主走出这个状态。只能说这个应用的开发者的设计不合理,UI 线程不应该执行非常耗时的操作。那么,非常耗时的操作应该在哪里执行呢?

【注意】

  UI 界面的更新是以异步的方式进行。UI 线程首先会执行用户代码,然后如果这些代码使得 UI 界面的数据发生的改变,UI 线程将对其 UI 界面进行更新。这意味着,并不是每执行一条更改 UI 数据的代码,它都会在 UI 界面上马上生效。有时候,这会导致一些问题。

  UI 线程是单线程的,指的 UI 界面是只通过一个线程来完成它界面的更新,指的不是凡是涉及 UI 的程序只能使用一个线程。UI 应用相比于后台应用,只是多了几个与处理 UI 相关的线程而已,没什么额外的线程个数限制。

  如果想了解更多关于同步、异步、阻塞的知识,可见笔者的另一篇博客:同步阻塞、同步非阻塞、异步阻塞、异步非阻塞:https://blog.csdn.net/wangpaiblog/article/details/117236684

JavaFX 中使用多线程

  为防止 UI 界面被阻塞,又因为 UI 线程是单线程的,因此应该选择在其它线程执行非常耗时的操作。可以选择当需要执行非常耗时的操作时,新开一个线程,将此非常耗时的操作放到新开一个线程去执行。

  在 JavaFX 中使用多线程一般使用两个类:ExecutorService、Task<Integer>。Task<Integer> 有一个方法 call,可以在这个方法去执行耗时操作。具体代码如下:

// 假设方法 someJavafxFun 位于 JavaFX 的某个组件的定义中public void someJavafxFun() { ExecutorService executor = Executors.newCachedThreadPool(); Task<Integer> task = new Task<>() { @Override protected Integer call() { // TODO 执行耗时操作 return null; // 如果需要结果反馈,可以在此处提供反馈值 } }; /** * 如果不需要结果反馈,也可以直接使用 executor.execute(task); * * 可以使用 result.get() 来获取上面的反馈值。但这个方法是同步阻塞的 */ var result = executor.submit(task); /** * 方法 getWindow() 获得的其实是 Stage。此段代码是用于在应用关闭时回收资源。 * * 对于真正的程序,方法 setOnCloseRequest 要设置在 Stage 被创建处。 * 因为方法 setOnCloseRequest 会覆盖其它 setOnCloseRequest 的效果,所以此方法只能执行一次。 * 为了达到这个效果,需要将 task 与 executor 设置成全局的,或者将其封装在一个全局静态方法中 */ this.getScene().getWindow().setOnCloseRequest(event -> { if (task != null) { task.cancel(); } if (executor != null) { executor.shutdown(); } Platform.exit(); });}

  Task<T> 是 JavaFX 的一个类,它继承至 FutureTask<T>。而 FutureTask<T>、ExecutorService 均为原生的 Java 多线程中的类,后续的操作均可依照 Java 多线程理论中的流程来完成。

【附】

  可以使用 Thread.currentThread().getName() 来查看某代码位于的线程。如下。其中,JavaFX 的 UI 界面所在的线程名为 JavaFX Application Thread。

System.out.println("【编号xxx】 执行本代码 XXX 的线程是:" + Thread.currentThread().getName()); JavaFX 中保证 UI 线程安全

  JavaFX 中的 UI 和大多数其它的编程语言中的一样,不是线程安全的,因为它是单线程的。在单线程中无需考虑线程安全的问题,但在多线程中需要考虑。介于本文讨论的重点,这里不打算解释什么是线程安全。那么,如果在 JavaFX 中使用了多线程,如何保证 UI 线程安全呢?

  在 JavaFX 中,可以在 UI 之外的线程中,使用方法 Platform.runLater 来执行与 UI 直接相关的操作。如下:

Platform.runLater(() -> {/* // TODO 更新 UI 数据的代码 */});

  注意:为保证 UI 界面的流畅,只需将与 UI 直接相关的代码置入上述的方法 Platform.runLater 中,不要在此方法中放多余的代码,否则就失去了使用多线程的意义。

总结与补充

为保证 UI 界面的流畅,需要将某些代码放入新建线程中,这些代码需要同时满足以下条件:

非常耗时或执行时间不能保证最坏结果也符合要求

与操作 UI 数据不直接相关

并非与 UI 数据强同步。

例如:如果 UI 需要请求一个资源,如果该资源不能获得,UI 就会崩溃,那么获取该资源的代码不能放在新建线程中,除非可以保证此线程与 UI 线程可以同步。因为,在不使用任何机制的情况下,新建的线程都是非阻塞的,如果选择将获取该资源的代码放在新建线程中,在这种情况下,UI 中请求资源的方法会立即返回,这个时候获取到的是这个资源的初始值(一般是 null)。也就是说,如果选择将获取该资源的代码放在新建线程中,相当于直接注释掉了新建线程获取资源这部分的代码。

为保证 UI 的线程安全,在其它线程不能直接更改 UI 的数据,必须将更改 UI 数据的代码传于方法 Platform.runLater 中运行。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。