JAVA多线程(MultiThread)的各种用法
创始人
2024-02-21 09:02:58

多线程简单应用

单线程的问题在于,一个线程每次只能处理一个任务,如果这个任务比较耗时,那在这个任务未完成之前,其它操作就会无法响应。

如下示例中,点击了“进度1”后,程序界面就没反应了,强行拖动容器后变成了“无响应”。

使用线程之前

 

 其原因是这段循环代码处于独占状态,这里并没有给其它代码执行的机会,包括接收界面更新的后台消息,导致应用程序处于一个假死的状态。只有等这个循环退出后,才可以进行其它的操作。

        for (double i = 0; i < 100.0; i++) {try {Thread.sleep(20);} catch (InterruptedException e) {throw new RuntimeException(e);}progressBar0.setProgress(i);}

 接下来,我们把这个循环代码放到一个单独的线程中,这样的话就可以不用占用主线程,等这个线程执行完了,再通知主线程更新界面。

代码改动后,变成这样:

        Executors.newSingleThreadExecutor().submit(() -> {for (double i = 0; i < 100.0; i++) {try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}final double p = i;Platform.runLater(() -> {progressBar0.setProgress(p / 100);});}});

 这里使用了Excutors工具类(JDK1.5以后引入),使用方法和new Thread()大同小异,只不过Executors里默认使用线程池,可以降低不必要的线程开销。

运行效果:

 

使用线程之后

 多线程的数据同步

我们对两个进度条进行计数,最上边的标签显示总和,下边一个标签对应一个进度条。结果中可以发现,两个进度条的计数是正确的,但总和却错了。

 

 我们看下进度1的代码(进度2的代码相同,仅仅标签和进度条的id不一样):

        Executors.newSingleThreadExecutor().submit(() -> {for (double i = 0; i <= 100.0; i++) {var c = count;try {Thread.sleep(20);} catch (InterruptedException e) {throw new RuntimeException(e);}c++;count = c;final double p = i;Platform.runLater(() -> {progressBar0.setProgress(p / 100);label0.setText(count + "");label1.setText(p + "");});}});

count是个全局变量,两个线程同时向这个变量赋值。因为中间有一个sleep()操作,所以在取得count的值之后,并不能确保count没有发生改变,如果得到count是的时候是1,等sleep()完后,可能是3,而此时还在1的基础上累加,最后将2再赋给count,于是误差就产生了。

为了解决这个问题,我们可以在累加完之前,不允许别的线程去修改count的值,大家共同拥有同一把钥匙,我拿钥匙了,别的就在外边等着。

 我们给计数部分的代码,套一层synchronized 块,此时HelloController.this就是同步锁的钥匙,哪个线程先执行到这个语句,就代表着拿到了钥匙,然后继续执行后边的语句,别的线程则需要停留在synchronized 的行,直到前边的线程已经退出语句块。

                synchronized (HelloController.this) {var c = count;try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}c++;count = c;}

 加上同步操作后,我们再下运行效果:

线程同步

 原子变量

对于计数器这东西,我们可以使用原子变量。这样,我们就不需要在自增自减上加锁,可以提升代码的性能。

定义一个整型的原子变量:

private AtomicInteger count = new AtomicInteger(0);

 修改同步代码:

Executors.newSingleThreadExecutor().submit(() -> {for (double i = 0; i < 100.0; i++) {try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}var c = count.incrementAndGet();final double p = i;Platform.runLater(() -> {progressBar0.setProgress((p + 1) / 100);label0.setText(count + "");label1.setText((p + 1) + "");});}});

 关于死锁

在使用多级锁的时候,容易发生死锁。

死锁示例:

    private Object locker1 = new Object();private Object locker2 = new Object();@FXMLprotected void onDeadLockButtonClick() {new Thread(() -> {synchronized (locker1) {System.out.println("Thread 1 in locker1");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2) {System.out.println("Thread 1 in locker2");}}}).start();new Thread(() -> {synchronized (locker2) {System.out.println("Thread 2 in locker2");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1) {System.out.println("Thread 2 in locker1");}}}).start();}

 运行结果为,点击死锁后,两个线程都不再往下执行,处在锁死状态。

 线程重用

按照计算机操作系统原理,创建线程是有一定的开销的。不停地创建销毁线程,会造成系统性能下降,所以尽量创建少的进程,并反复加以利用。原理是创建一个工作进程,并让其进入等待状态,在需要的时候发出通知,让工作进程再次进入工作。

实现代码:

    private Object locker3 = new Object();@FXMLprotected void onReUseThreadButtonClick() {synchronized (locker3) {locker3.notify();}}@FXMLprotected void onCreateThreadButtonClick() {new Thread(() -> {logs.appendText("Thread created...\n");synchronized (locker3) {while (true) {try {logs.appendText("Thread waiting...\n");locker3.wait();logs.appendText("All right. Next...\n");Thread.sleep(300);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}).start();}

 运行效果:

 线程的汇集操作

可以将一些线程并行计算的结果,汇总到同一个线程。假如电脑的CPU核心数和线程数较多,可以拆分对应个数的线程进行并行计算,再将结果汇集到主线程。原理使用Thread.join()方法。

代码:

        new Thread(() -> {Thread t1 = new Thread(() -> {try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}Platform.runLater(() -> {logs.appendText("t1 exited....\n");});});t1.start();Thread t2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}Platform.runLater(() -> {logs.appendText("t2 exited....\n");});});t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}Platform.runLater(() -> {logs.appendText("current exited....\n");});}).start();

 运行效果:

 

相关内容

热门资讯

【看表情包学Linux】进程地...   🤣 爆笑教程 👉 《看表情包学Linux》👈 猛...
吉字五行及吉凶 吉字五行中代表... 五行解析在文化中,五行是非常重要的概念之一,在这里解析一下五行对于人们生活的影响。首先,金属代表的是...
月老姻缘灵签内容详解大全 月老... 月老灵签 姻缘签44签 求解签君尔目下之人。本是可心满意足之人。焉知后来之人。一个比一个更美好。就此...
六爻排盘蛇 六爻排盘预测绝招 ... 六爻排盘结果怎么看纳甲六爻在线排盘姓名:出生年:1981性别:男占事:起卦方式:手动摇卦公历时间:2...
吉凶由情绪决定 每日吉凶 每月... 情绪的力量情绪是我们生活中一个重要的组成部分。我们每天都会通过不同的方式感受到情绪的存在,而情绪的质...
吉凶悔吝的解释是什么 风水形势... 吉凶悔吝的解释是什么从古至今,人们对于吉凶悔吝都有着不同的看法。所谓吉,是指好的运气,让人们沾沾自喜...
如何看每日生肖运势 每日生肖运... 背景说明每个人都希望自己的运势越来越好,而对于人来说,生肖运势是一个参考价值很高的判断标准。按照传统...
最准观音灵签21签解签 观音灵... 观音灵签21签解签-遵医嘱,健康长寿观音精神签证是中国民间宗教信仰的重要形式,也是一种广泛流传的祈祷...
梦见红裤子被水冲走 梦见河里洗... 梦见红裤子被水冲走红裤子是一种比较鲜艳的颜色,在梦中出现可能代表着某种情绪或状态。而被水冲走则更加具...
十二星座对象配对 12星座最佳... 12星座配偶标准白羊座:温柔善良的人乐观单纯的白羊座在恋爱时喜欢另一半无条件的宠爱自己,另一半对自己...
带水又带土的名字女孩名字有哪些... 含水和土的字有哪些含水的字:淦、澜、浸、泼、滴、没、汪、沸、鸿、沔、浩、渣、溢、潺江、注、漭、淬、澧...
六爻失物卦 在线占卜失物 六爻... 六爻占卜 寻找失物公历起卦时间:2012年12月24日9时44分(按公历时间起卦)农历:仁辰年十一月...
天网今日生肖运势 每日特吉生肖... 天网今日生肖运势天网今日,十二生肖依旧是重要的关键词之一。根据传统文化和民间信仰,每个人都属于一个生...
六爻代表书籍 六爻预测好的书籍... 学六爻的书籍那些比较经典,最好适合初学者的。从古至今六爻类的书流传于世的非常少,六爻类最经典的几本书...
十二星座女生专属花卉 小葩画1... 狮子座的女生喜欢什么样的花1.狮子座的女生喜欢鲜艳、华丽、高贵的花。2.狮子座的女生通常有着自信、热...
客厅风水禁忌及化解 客厅推拉门... 客厅风水禁忌及解决方案客厅是家庭中最重要的空间之一,也是最容易受到风水影响的空间之一。在客厅里,我们...
吉凶参半牛兔在含义 牛兔相冲到... 吉凶参半牛兔在啥意思吉凶参半牛兔在是指属牛的和属兔的结婚以后生活吉凶各一半。丑牛与子鼠六合,因此最宜...
各生肖属相的车牌号码吉凶对照表... 十二生肖与车牌号的佳搭配 十二生肖车牌号吉凶对照表通常每个人的黄道十二宫都会影响车牌号码的运行模式,...
客厅西部尖角的风水 客厅有棱角... 客厅西南角最好的风水是什么?客厅西南角最好的风水是什么?客厅西南角最好的风水是什么?房子的方形风水是...
带昶字的女孩名字 带滢的女孩名... 长字命名的寓意及意义长字命名的寓意和意义是正直、坚强、努力、阳光、前途似景、忠诚。长是一个通用词。长...
六壬怎么算命 六壬掐指神算金口... 什么是六仁?刘仁是中国古代的算命方法,起源于汉代,是中国道教学派的经典之一。刘仁包括六个神:天乙、天...
四月二十九生肖运势 十二生肖鸡... 女1993农历四月二十九早上十点生辰八字是什么如何出生时间:公历 1993年 6月 18日 10点本...
带心字的游戏女孩名字大全集 游... 2020男孩怎么起名有内涵 带心字的男孩名字大全心繁体:心起名五行:金姓名学笔画:4画简体笔画:4画...
八字鬼谷子算命 鬼谷子精髓50... 什么是八字鬼谷子算命?八字鬼谷子算命,又称李静算命,是中国传统的民间算命方式。八字鬼谷子算命起源于六...
十二星座下个月的运势女生 十二... 白羊座下个月的运势女生白羊座女生本身就充满了无限活力和热情,下个月的运势也不会让你失望。职业上可能会...
号令天下手机吉凶预测 号令天下... 手机号怎么算吉凶?用最后四个手机号码除以80,然后减去整数部分(只留小数),再乘以80,就会得到一个...
命理十二生肖今年运势 明天运势... 命理十二生肖今年运势今年每年都有不同的转瞬即逝的岁月。对于不同的黄道十二宫来说,它每年都有自己独特的...
八字长生好吗 八字中帝旺到长生... 八字日坐长生一定富吗丁火曰元生于未月,余气通根,年支丙火也能助身,但于上两透旺食,生财耗身过甚,故命...
带日的名字女孩名字大全 起名带... 日字旁边的女孩名字大全日字旁边的女孩名字推荐1、诗晗、慧曦、Xi、仲晴2、小芸、小娟、会晴、若昕、敏...
号令天下固话号码测吉凶 查电话... 周易81测手机号码吉凶,号令天下手机号码测吉凶提起周易81测手机号码吉凶,大家都知道,有人问天下手机...