第68篇:javafx编写扫描器UI界面的线程死锁问题及坑点总结

news2024/9/23 13:30:48

f548e59533febb66a2a272a2667c5ad2.png

 Part1 前言 

大家好,我是ABC_123。之前编写工具的图形界面都是用swing框架来实现,但是swing框架已经10几年没有更新了,很多控件使用起来特别麻烦,然后界面美工需要花费很大精力。为了跟上知识更新的节奏,ABC_123最近花时间学习了javafx(有swing的基础,学习javafx上手是非常快的),于是花时间把之前的扫描工具的图形界面换成了javafx,但是在多线程操控图形界面控件的时候,遇到了一系列线程死锁问题,为了解决这些问题,ABC_123又踩了一大堆坑,今天当做笔记,自我复盘的同时,也把经验分享给大家。

 Part2 技术研究过程 

  • 扫描器设计思路

我想实现如下功能:burpsuite抓到一个数据包之后,点击右键弹出菜单,将指定的扫描任务发送到服务端的“扫描任务队列”去进行扫描,与服务端通信是通过socket实现的。

a196e67f8f1d289d07f4648744ee4b77.png

“扫描任务队列”会监听一个端口,收到burpsuite的任务请求之后,会新建一个Tab标签,然后每个任务分配10个线程扫描,也就是说,每一个Tab标签对应着一个扫描任务,每个扫描任务都是10个线程在运行。在编写这个扫描工具过程中,踩了一大堆坑,接下来把解决方法分享给大家。

70b629dc3e586ead4764801dc22460b4.png

  • 坑1:多线程中添加一个Tab标签直接报错

刚开始用多线程操作javafx控件就遇到了一个报错,向图形界面添加一个图形控件时,报错提示“Not on FX application thread; currentThread = Thread-3”,大致意思是“当前线程不是JavaFX应用程序线程”。

362c42c9c996922a9f223d575c7a2061.png

经过一系列搜索发现,操控javafx的图形控件需要用以下Java语句包裹起来就可以了Platform.runLater(() -> { });。

2c2339a0f143b1ac41c5f964dbd09bd1.png

  • 坑2:Platform.runLater与ReentrantLock可重入锁的选择问题

进过前面探讨我们知道,Platform.runLater保证javafx线程安全,ReentrantLock锁可以保证全局变量的线程安全问题。这就引出一个问题,对于如下代码,当多线程操控qq.readResCount = qq.readResCount + 1;这个全局变量的值时,它本身已经被Platform.runLater(() -> {});包裹了,用不用加上再ReentrantLock锁保证线程安全呢?

0c35b75c0b3da378f5d1904f26ce81ee.png

在网上各种百度谷歌,然后询问身边的人,对这个问题说法不一,所以我干脆就自己编写一个测试代码,实战测试一下吧。

 1  全局变量不加锁的错误写法

首先回顾一下多线程资源竞争问题,如下代码运行之后出现错,因为多线程操控全局变量没有任何限制,很明显会出现竞争问题。正常输出是7、8、9、10随机出现,但是却出现了多个10及多个11的情况,输出结果明显不正确。接下来分情况测试一下,探究一下Platform.runLater与ReentrantLock锁应该怎么配合使用

7176f0b1085c49a2290177bfe4e38dc9.png

 2  Platform.runLater不用,ReentrantLock锁使用

首先看这种情况,运行后马上各种报错,说明ReentrantLock锁无法保证javafx控件的线程安全问题

acedbdc57b8f2e3e8636113ca6661596.png

 3  Platform.runLater使用,ReentrantLock也使用

接下来看这种情况,运行后非常稳定,没有问题,但是对于Quanjv.count全局变量的改变,ReentrantLock锁是否可以去掉呢?

2314e69eef52b53d71323e0bf12bdd88.png

 4  把ReentrantLock锁去掉

接下来看这种情况,把ReentrantLock锁去掉,由Platform.runLater保护Quanjv.count,发现程序运行之后,没有问题,说明Platform.runLater在保证javafx控件安全时,也能保证全局变量的线程安全。

9030f73e5ea2017884493f1777602544.png

通过以上的测试,最终我们得出一个结论:

1. Platform.runLater(() -> {});不但可以保证Javafx控件线程安全,同时也可以保证全局变量数据的线程安全。

2. ReentrantLock锁可以保证全局变量数据的线程安全,但是对于保证javafx控件线程安全毫无用处。

  • 坑3:javafx控件取值和修改值是否需要加锁

在网上搜索了很多说法,答案不一,那我们还是编写测试代码,来测试一下吧。

 1  javafx控件取值过程测试

为了保证测试效果,我们设置100个线程同时操作textThread方法,高并发可以提升线程安全问题报错的机率。经过测试我们发现,对于TextArea的多线程取值过程,不用加Platform.runLater(() -> {});,也可以保证线程安全。

4caf88c0126b46e73e7711c2b922af7a.png

 2  javafx控件修改值过程测试

接下来再添加一行修改javafx控件文本框的代码:Quanjv.textarea.setText("test");,发现在100个线程操作下程序立马报错。

ddd0b6d00748514b8604afc447618bf2.png

接下来对修改javafx值的代码用Platform.runLater(() -> {});包裹起来,程序运行之后发现,100个线程下没有任何错误。

1853f72b1ff5f11b1d4cf7d1fa291f89.png

最终得出结论,javafx的控件的取值过程基本上不涉及线程安全问题,但是对于javafx组件的任何修改,必须考虑线程安全问题

  • 坑4:Tabs标签移除问题

当发送一个扫描任务队列时,TabPane会新建一个Tab标签,每个标签10个线程运行,双击Tab标签,就会停止该任务的多线程扫描,Tab标签的标题会提示“停止..”字样,直到所有活动线程安全结束,该标签关闭。

386796974c5f70c6f478e0cea3cae6b5.png

代码是按照如下格式编写的,用Platform.runLater(() -> {});代码包裹起来,按理上不存在线程安全问题。

d37acf692b9d50862586bcd47d5d2f17.png

但是实测结果,经常在如下代码中,出现报错问题,导致程序崩溃,所有扫描任务停止。

27c780b42841245113e89d6334e12d0c.png

这是一个隐藏非常深的线程安全bug,在一天中会不定时的出现几次,而且没办法复现,让我大伤脑筋。后来我终于想明白了,一个TabPane是由多个标签组成的,当你双击关闭其中一两个标签时,tabPane的所有索引id都变了,而另一个线程对于Tab标签的for循环操作还在进行当中,而且还是按照原始的索引去遍历,而原始的索引都变了,造成了程序的崩溃。

  • 坑5:jdk8与jdk11等高版本不兼容

举个例子,对于以下这个图形界面,是使用scenebuilder20.x版本拖拽出来的,看着没有问题。

2679c5bdd7ea4250dcfe840d5727e89f.png

但是如果用sceneBuidler 8.x版本打开,整个界面的很多控件的位置都乱了,重叠在一起。

9a021b4fde454fffe6b98e7f3ab63f59.png

最终得出结论:javafx的图形界面在jdk8及其它高版本jdk是存在兼容性问题的,Scenebuilder8.x适用于jdk8版本的图形界面拖拽,Scenebuilder20.x适用于jdk11到jdk20的版本的图形界面拖拽。

  • 坑6:fmxl行数过多会很卡

用Scenebuilder拖拽的方法画图形界面,感觉特别方便,但是也有问题。比如说我写的如下工具,fxml文件已经快1500行了,此时再用scenebuilder拖拽会特别卡。

6c185bf176eae77b20e87833ad73c41d.png

最终没有办法,我将其中一个TabPane界面的Tab标签删掉,用纯java代码编写,有时候用纯java代码写图形界面比拖拽是要方便的。以下这个界面,按钮控件特别多,每个按钮的功能类似,于是我用一个Map集合放置每一个按钮标题和按钮事件中用到的关键值,然后用一个for循环,遍历Map集合添加Button按钮组件,很快搞定这个界面,比Scenebuilder拖拽图形界面还方便。

我们也可以发现,通过java纯代码编写的图形界面,比Scenebuilder拖拽的看起来要规整,因为很多时候拖拽会在控件对齐方面会有误差,这就是java代码编写图形界面的好处。

351ce488500199726832ea1f76e79233.png

  • 坑7:javafx在jdk11至jdk17的编译问题

按照正常的编写javafx程序的流程,idea 2022版本编译出来的jar包,有时候会提示找不到主类,有时候会提示缺少JavaFX运行组件。对于jdk8下的javafx的编译,很简单,直接编译成一个jar包就可以在jdk8上双击运行,因为jdk是自带javafx库的,但是对于更高版本的jdk,比如说jdk11或者jdk17,默认是不带javafx库的,所以就引发出各种各样的问题。

9eb12afb4d864bac8f7b1aa720055265.png

网上有很多解决这个问题的方法,但是说法不一,于是我经过各种测试,得出如下步骤,可以保证编译的jar包能够正常运行。

首先使用idea 2022新建项目,JDK选择大于等于jdk8的版本即可,小于jdk8不支持javafx。

18553ecb1155ca58c2b1fcc21c743ba4.png

可以看到idea 2022版本,已经自动在pom.xml文件中添加了javafx库了。所以我们无需添加额外的javafx的jar包,有的解决方案说是要从javafx官网下载jar包导入,实际上是没必要的。

0752828a36a45812f65821a4e6074a24.png

接下来是最重要的一个步骤,我们需要新建一个主类,按照如下格式编写:

0783dc9aba012959de25aa85313ead0a.png

2fcec0b8ec7cb7e39821875ac39645e4.png

接下来需要设置如何去编译jar包文件,主类需要选择我们新建的JavaFXBootstrap类,记住一定要删掉main\resources

e6c513357233a6952e0c0c95eabf3c6c.png

如下图所示,这是正确的idea配置。按照上述的操作编译出来的jar包,可以完美运行而不报错。

55ca2177f9909d5ce36f6df9407025a2.png

 Part3 总结 

1. 遇到线程安全问题,最好的方法就是写个demo程序在高并发下反复测试。

2. 其余的总结及结论都在文章里每一部分给出了,这里不再重复。

b9495d7dbe5b999b645104d2ce0d9e86.png

公众号专注于网络安全技术分享,包括APT事件分析、红队攻防、蓝队分析、渗透测试、代码审计等,每周一篇,99%原创,敬请关注。

Contact me: 0day123abc#gmail.com(replace # with @)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/758894.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

语言模型的自洽性思维链推理技术

论文标题:Self-Consistency Improves Chain of Thought Reasoning in Language Models 论文链接:https://arxiv.org/abs/2203.11171 论文来源:ICLR 2023 一、概述 尽管语言模型在一系列NLP任务中展现出了显著的成功,但它们在推理能…

django使用channels实现webSocket启动失败

问题描述 使用channels启动ASGI结果却是普通启动,如下: Watching for file changes with StatReloader Performing system checks...System check identified no issues (0 silenced). July 15, 2023 - 18:23:49 Django version 4.2, using settings s…

chatGPT 和AlphaGo下围棋,谁赢?垂域大模型有戏么?

这边来的少,但发个文章通报下近况,长期做AI产研、投融资工作后,后续主要在企业数字化与大模型结合的方向上,后续进展还是请关注:琢磨事。 上一篇提到最终大模型的格局很可能是有一个偏通用大模型,比如chatG…

Linux系统编程——文件(ioctl 函数)

文章目录 概念用户空间 ioctl驱动程序 ioctlioctl 在用户与驱动之间的协议——命令码实例分析ioctl-test.hioctl-test-driver.cioctl-test.c 概念 ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一…

GitOps自问自答

GitOps自提出以来受到很多关注,被认为是云原生最佳实践之一。这篇文章回答了关于GitOps的常见问题,帮助感兴趣的相关人员更好理解这一实践。原文: GitOps[1] 自从Weaveworks在2017年提出GitOps以来,已经在Twitter和KubeCon上引发了不少争议。…

Qt实现思维导图功能6『鹰眼视图』

前文链接:Qt实现思维导图功能5『纵向分布模式』 百度网盘体验地址: 链接:https://pan.baidu.com/s/1xotlkSPfG7E_37y_XPfDng 提取码:5li7效果图 1、动态演示效果: 思维导图-鹰眼视图 2、静态展示图片: 新…

Econ3107-econ5116-小组作业知识点精讲

对本文有疑问可以加微信 Tutor_0914联系。也可以访问我的个人辅导网站 : tutoryou 基本概念 option期权 期权(Option),是一种选择权,指是一种能在未来某特定时间以特定价格买入或卖出一定数量的某种特定商品的权利…

Linux 知识学习总结

常用命名 du 作用:显示指定目录或文件所占磁盘空间大小。 示例: du -h 以K,M,G为单位自动适配显示 lxlx-virtual-machine:~/test/video$ du -h 1.2G du -m 指定以1MB为单位显示 lxlx-virtual-machine:~/test/video$ du -m 12…

Python潮流周刊#11:如何使用 Golang 运行 Python 代码?

你好,我是猫哥。这里每周分享优质的 Python 及通用技术内容,大部分为英文,已在小标题注明。(标题取自其中一则分享,不代表全部内容都是该主题,特此声明。) 本周刊精心筛选国内外的 250 信息源&a…

​注意力机制中的掩码详解

注意力机制的掩码允许我们发送不同长度的批次数据一次性的发送到transformer中。在代码中是通过将所有序列填充到相同的长度,然后使用“attention_mask”张量来识别哪些令牌是填充的来做到这一点,本文将详细介绍这个掩码的原理和机制。 我们先介绍下如果…

(简单)设计哈希集合 Java

为了实现哈希集合这一数据结构,有以下几个关键问题需要解决: 哈希函数:能够将集合中任意可能的元素映射到一个固定范围的整数值,并将该元素存储到整数值对应的地址上冲突处理:由于不同元素可能映射到相同的整数值&…

SpringBoot读取配置的方式

读取配置的几种方式 Spring Boot提供了多种方式来读取配置,下面是其中几种常用的方式: 使用application.properties或application.yml文件:在Spring Boot项目的classpath根目录下,可以创建一个名为application.properties或appli…

oc基本控件3

UIButton // // ViewController.m // OcDemoTest // // Created by Mac on 2023/7/14. //#import "ViewController.h"interface ViewController ()endimplementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 1 创建按钮对象UIButton *button…

涂鸦智能打造专业家庭智能生活助手,实现人机交互升级

近年来,智能家居设备的品类不断拓展,同时,人们对AI与智能家居的联动愈发憧憬。自然语言交互是未来人机交互的主要趋势之一,其关键在于使AI具备主动理解信息的能力,让用户的交互更轻松。如何将智能场景的交互变得更“善…

MySQL-DDL-表结构操作-创建-案例

案例 根据页面原型/需求创建表(设计合理的数据类型、长度、约束) 具体操作 在idea中使用可视化图形界面创建 具体操作如下: 在该界面中进行属性的创建,进行属性名称、数据类型、约束、描述等信息的填写最终运行结果如下&…

800V高压电驱动系统盘点

2023年上海车展共有23家厂商的63个电驱动产品,经过梳理,本次展出的800V高压电驱动共有13款,可以说电驱动全面进入高压化。800V电驱动是一个系统性的话题,对于电机而言,挑战的方向主要围绕高速、高压、散热,…

替换空格

替换空格 请实现一个函数&#xff0c;把字符串 s 中的每个空格替换成"%20"。 题目给的测试用例里有以下限制&#xff1a; 0 < s.length < 14。 split() 把字符串分割为子字符串数组 例如&#xff1a; var txt"ABCD EFGH IJKL MNOP QRSTU VWXYZ"; v…

微信小程序下拉选择

微信小程序中下拉框选择一般的交互方式有以下两种 直接下拉选择点击选择框后&#xff0c;弹出浮层进行选择 下边分别介绍两种方式的实现。在微信小程序中&#xff0c;这两种实现都需要修改三个文件 js 文件&#xff1a;下拉选择逻辑的具体实现 wxml 文件&#xff1a;下拉组件…

C#正则表达式校验某个字符串是否是合格的email

C#正则表达式校验某个字符串是否是合格的email 可以借助正则表达式校验某个字符串是否是合规的电子邮箱。对于邮箱的正则表达式有严格的模式&#xff0c;如&#xff1a;^[a-zA-Z0-9_&*-](?:\\.[a-zA-Z0-9_&*-])*(?:[a-zA-Z0-9-]\\.)[a-zA-Z]{2,7}$ 对应的C#实现如下…

TCP编程流程和粘包

目录 1、TCP编程流程 2、粘包 1、TCP编程流程 socket() 是创建套接字&#xff0c;返回值为监听套接字描述符&#xff0c;有了套接字才能通过网络进行数据的传输。创建套接字的参数要指定服务类型&#xff0c;TCP协议使用的是流式服务&#xff08;SOCK_STREAM&#xff09;。 b…