前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第68篇:javafx编写扫描器UI界面的线程死锁问题及坑点总结

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

作者头像
ABC_123
发布2023-09-02 16:59:37
3040
发布2023-09-02 16:59:37
举报
文章被收录于专栏:网络安全abc123
Part2 技术研究过程
  • 扫描器设计思路

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

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

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

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

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

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

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

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

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

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

2 Platform.runLater不用,ReentrantLock锁使用

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

3 Platform.runLater使用,ReentrantLock也使用

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

4 把ReentrantLock锁去掉

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

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

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

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

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

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

1 javafx控件取值过程测试

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

2 javafx控件修改值过程测试

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

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

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

  • 坑4:Tabs标签移除问题

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Part3 总结

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 希潭实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Part3 总结
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档