您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

一个线程协作问题有很多解决方案,所以要在它被破坏之前除掉它。 《影视源码》

  • 时间:2022-10-29 03:35 编辑: 来源: 阅读:306
  • 扫一扫,手机访问
摘要:一个线程协作问题有很多解决方案,所以要在它被破坏之前除掉它。 《影视源码》
前言不知道大家有没有感觉到。小学初中读过的几本书,几篇文章,到现在都让你印象深刻。而是高中的知识在高考后逐渐消散,直至被遗忘。 我想说的是,我记得初中鲁迅《藤野先生》里的一段话,大意是我很久不联系,有时候想联系,但是写不下来,最后都结束了。 我找了个参考:我走的前几天,他让我去他家,给了我一张照片,照片背面写着两个字:“永别”,说希望把我的也给他。 但是这个时候我没有拍照;他让我以后拍照片发给他,并时不时通过书信告诉他情况。 自从离开仙台后,我已经很多年没有拍过照片了,而且因为情况很无聊,只是让他很失望,所以我不敢写信。 一年多了,谈不上了,所以虽然有时候想写封信,但是很难写。到目前为止,我没有发过一封信或一张照片。 从他这边来看,他走了之后好像就没有消息了。 其实写文章也是如此。很久不写了,就不想写了,但是心里一直记着这样的事,玩起来也不是很舒服。今天随便写写吧,找状态,因为现在的文章可能会在博客和微信官方账号发表,比如博客,一般来说,但是在微信官方账号的话,人的素质要求一般会高一点。这样一来,为了追求高质量,就不必去找几个厉害的技术点,或者写作者在写作前研究透彻,就会导致少量的创意难以产生。由于可能很简单,不值得发到微信官方账号。其实很多时候。 这一切说的都是我自己,或者说是我剩下的刚开始写技术类微信官方账号的同学,不用去想写的有多好才发出来。本来大家都是一步一步走过来的,各种大佬也不是一下子就成了大佬。把自己的学习过程和成长过程发出来,大家就知道了:哦,老板都是这样的菜,哈哈。 比如最近,我看到了少量的算法。一开始也是10道算法题。所有的都要看答案好吗? 让我们言归正传。最近在网上看到一个面试题目,挺有意思的。大意如下:好的,你可以先了解一下这个话题。这里启动了两个线程A和B。但是,虽然说A在B之前开始,但是并不能确定线程A的逻辑可以在线程B之前执行,所以,我这里的意思是,线程A和B的执行顺序互不干扰。 另外,既然是面试问题,常规做法自然没必要,比如让B先睡几秒钟,等等。如果是这样,面试可能就结束了。 好了,我们开始分析解决方案。 可见性保证程序中定义了一个全局变量,var = 1;;线程A会将这个变量修改为2,而线程B会在变量为2时执行自己的业务逻辑。 所以,在这里,首先我们要做的是,首先说说var使用volatile decoration保证多线程操作的可见性。 public static volatile int var = 1;通过对可见性保证的分析,我们知道为了实现目标,实际上需要保证A中var+1的运算需要在b之前进行。 但是,现在的问题是,如果同时启动两个线程,不知道谁先启动,谁后结束。如何保证A先执行,B后执行?让线程B先不执行,大概有两种思路,一是阻塞线程,二是不阻塞线程。如果是这样,我们可以考虑如何阻塞一个线程。 大概:同步。当不能获得锁时,它阻塞Java . util . concurrent . locks . reentrant lock # lock。当锁不能被获得时,它阻塞object.wait,但是由于少数条件不满足,它不能被执行。调用wait将释放锁并进入等待队列。挂起运行java . util . concurrent . locks . condition.await的线程,类似于object.wait,只不过object.wait在jvm级别和c++中实现,condition . await在Java语言中在jdk级别实现threadA.join(),这个线程会在对应的threadA完成后继续运行;一个线程没有完成,那么当前线程被阻塞;CountDownLatch#await,对应状态不为0时,阻塞信号量# acquire();当状态为0时(即剩余令牌为0时),阻塞其他阻塞队列、FutureTask等。如果线程没有被阻塞,一般可以进入while循环,循环的退出条件可以由线程A修改,线程A修改后,线程B跳出循环。 示例:volatile boolean stop = false而(!停止){...}上面说了这么多,我们还是实际手写吧。 错误方法1——基于等待以下思路是基于等待和通知;;线程B直接等待,线程A修改变量后通知。 public class global 1 { public static volatile int var = 1;公共静态最终对象监视器= new Object();public static void main(String[]args){ Thread a = new Thread(()-& gt;{//1 global 1 . var++;// 2同步(monitor){ monitor . notify();} });线程b =新线程(()-& gt;{ // 3同步(monitor){ try { monitor . wait();} catch(interrupted exception e){ e . printstacktrace();} }//4 if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });a . start();b . start();}}你觉得这个代码行得通吗?其实是行不通的。 由于实际顺序可能是:线程A-1线程a-2线程B-1线程B-2,当线程A-2的时候,线程A去通知,但是此时线程B还没有开始等待,所以此时的notify没有作用:没有人在等待,通知一个锤子。 这个方案如何修改才能奏效?也就是修改线程A的代码,不要急着通知,先等等。 线程a =新线程(()-& gt;{ global 1 . var++;试试{ TimeUnit。seconds . sleep(2);} catch(interrupted exception e){ e . printstacktrace();}同步(monitor){ monitor . notify();} });但是,这显然是不合适的,有出轨的嫌疑,也不优雅。 错误方法2-基于条件signal import Java . util . concurrent . time unit;导入Java . util . concurrent . locks . condition;导入Java . util . concurrent . locks . reentrant lock;public class global 1 { public static volatile int var = 1;public static final reentrant lock reentrant lock = new reentrant lock();公共静态最终条件Condition = reentrant lock . new Condition();public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;final reentrant lock lock = reentrant lock;lock . lock();请尝试{ condition . signal();}最后{ lock . unlock();} });线程b =新线程(()-& gt;{ final reentrant lock lock = reentrant lock;lock . lock();请尝试{ condition . await();} catch(interrupted exception e){ e . printstacktrace();}最后{ lock . unlock();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });a . start();b . start();}}这个方案使用Condition对象来实现对象的通知和等待效果。 当然,这个也有同样的问题。 正确解决方案1-基于错误满足方法2的改进。我们来看看,之前问题的根源是我们的线程A去通知线程B的时候,有可能线程B还没有开始等待,所以此时通知无效。 那么,我们能等到线程B开始等待,然后通知吗?线程a =新线程(()-& gt;{ global 1 . var++;final reentrant lock lock = reentrant lock;lock . lock();尝试{ // 1 while(!reentrant lock . has waiters(condition)){ thread . yield();} condition . signal();}最后{ lock . unlock();} });1码,就是这个思路。发出信号前,判断当前条件下是否有等待线程。如果没有,就是无限循环;如果是,就执行信号。 这种方法在实际测量中是可行的。 正确的解决方案2对于正确的解决方案1,更改一个api,它就成为正确的解决方案2。thread a = new thread(()-->:{ global 1 . var++;final reentrant lock lock = reentrant lock;lock . lock();try {//1 while(reentrant lock . getwaitqueuelength(condition)= = 0){ thread . yield();} condition . signal();}最后{ lock . unlock();}});在这里,得到条件下等待队列的长度。如果是0,说明没有等待者,是一个无限循环。 正确解3——在信号量的开头,我们用状态0初始化一个信号量。当线程B去获取信号量时,它会阻塞。 然后我们的线程A释放一个信号量,然后线程B可以继续执行。 public class global 1 { public static volatile int var = 1;公共静态最终信号量信号量=新信号量(0);public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;semaphore.release()。});a.setName("线程a ");线程b =新线程(()-& gt;{ try { semaphore . acquire();} catch(interrupted exception e){ e . printstacktrace();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}正确答案4——基于CountDownWatch公共类global 1 { Public static volatile int var = 1;public static final CountDownLatch CountDownLatch = new CountDownLatch(1);public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;countdownlatch . count down();});a.setName("线程a ");线程b =新线程(()-& gt;{ try { countdownlatch . await();} catch(interrupted exception e){ e . printstacktrace();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}正确解决方案5——基于BlockingQueue,这里使用ArrayBlockingQueue,其他的阻塞队列也是可以的。 导入倒计时。CountdownTestpublic class global 1 { public static volatile int var = 1;public static final ArrayBlockingQueue ArrayBlockingQueue = new ArrayBlockingQueue & lt;Object & gt(1);public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;arrayblockingqueue . offer(new Object());});a.setName("线程a ");线程b =新线程(()-& gt;{ try { arrayblockingqueue . take();} catch(interrupted exception e){ e . printstacktrace();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}正确解法6——基于FutureTask,我们还可以让线程B等待一个任务的执行结果;线程A将var修改为2后执行该任务,任务完成后通知线程B继续执行。 public class global 1 { public static volatile int var = 1;public static final future task future task = new future task & lt;Object & gt(新的可调用& ltObject & gt(){ @Override public Object call()抛出异常{ system . out . println(" callable task ");返回null} });public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;future task . run();});a.setName("线程a ");线程b =新线程(()-& gt;{ try { future task . get();} catch(interrupted exception | execution exception e){ e . printstacktrace();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}正确解法7——这个基于join的可能是最简洁直观的,哈哈 也是群里同学提供的解决方案。真的很有才!public class global 1 { public static volatile int var = 1;public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;});a.setName("线程a ");线程b =新线程(()-& gt;{ try { a . join();} catch(interrupted exception e){ e . printstacktrace();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}正确解法8——基于CompletableFuture这个和第六个差不多。 这一切都是基于未来 public class global 1 { public static volatile int var = 1;公共静态最终CompletableFuture & ltObject & gtcompletableFuture = new completableFuture & lt;Object & gt();public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;completablefuture . complete(new Object());});a.setName("线程a ");线程b =新线程(()-& gt;{ try { completablefuture . get();} catch(interrupted exception | execution exception e){ e . printstacktrace();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}}非阻塞-正确解决方案9-忙等待代码量也小,只要变量为1时线程B无限循环。 public class global 1 { public static volatile int var = 1;public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;});a.setName("线程a ");线程b =新线程(()-& gt;{ while(var = = 1){ thread . yield();} if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}非阻塞-正确解决方案10-繁忙等待有多种方案。反正不满足某个条件的时候,我们是不会屏蔽自己的。阻塞会释放cpu,我们就是不想释放cpu。 比如也可以做以下。 public class global 1 { public static volatile int var = 1;public static final atomic integer atomic integer = new atomic integer(1);public static void main(String[]args){ Thread a = new Thread(()-& gt;{ global 1 . var++;atomic integer . set(2);});a.setName("线程a ");线程b =新线程(()-& gt;{ while(true){ boolean success = atomic integer . compare andset(2,1);if(成功){ break} else { thread . yield();} } if(global 1 . var = = 2){//做点什么;system . out . println(thread . current thread()。getName() +"好样的");} });b.setName("线程b ");a . start();b . start();}}总结暂时写好了,但是计划还是很多的。可以开动脑筋,集思广益!最后,如果看完还有什么不明白的,可以在下面留言讨论。感谢您的观看。 记得关注我,觉得文章对你有帮助就给我赞!作者:每日参考链接:https://www.cnblogs.com/grey-wolf/p/13737109.html


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)
【技术支持|常见问题】你正确使用https了吗? [php源码](2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部