济宁市做网站的公司,米拓网站建设步骤,中小企业网站建设需要注意什么,手机网站跳转代码7月8日
以下文章来源于编程新说 #xff0c;作者编程新说李新杰
多线程的问题都曾经困扰过每个开发人员#xff0c;今天将从全新视角来解说#xff0c;希望读者都能明白。
强烈建议去运行下文章中的示例代码#xff0c;自己体会下。
问题究竟出在哪里#xff1f; 一个…7月8日
以下文章来源于编程新说 作者编程新说李新杰
多线程的问题都曾经困扰过每个开发人员今天将从全新视角来解说希望读者都能明白。
强烈建议去运行下文章中的示例代码自己体会下。
问题究竟出在哪里 一个线程执行固然是安全的但是有时太慢了怎么办 老祖宗告诉我们“一方有难八方支援”那不就是多叫几个线程来帮忙嘛好办呀多new几个不就行了又不要钱。这样能管用吗继续往下看。 俗话说“在家靠父母出门靠朋友”。有了朋友的帮助就会事半功倍。是这样的吗 不一定如果朋友“不靠谱”结果竟是在“添乱”。于是就演变为“不怕神一样的对手就怕猪一样的队友”。可见“人多力量大”纵然是对的但也要配合好才能成事。 人和人是朋友那线程和线程也是“朋友”如果多线程之间不能配合好的话最终也会变为“猪一样的队友”。事实证明这也不是一件易事。且容我慢慢道来。 开发是一门技术管理是一门艺术。也许你正想带着兄弟们大干一场可偏偏就有人要辞职。或者你付出了这么多但别人从来没有感动过。为什么会这样呢 因为你面对的是人。每个人都是独立的个体有思想有灵魂有情感有三观。能够接受外界的“输入”经过“处理”后能够产生“输出”。 说白了就是会自主的分析问题并做出决定。这叫什么呢答案就是主观能动性。 拥有主观能动性的物体比如人你需要和它协商着或配合着来共同完成一件事情而不能“强迫”它去做什么因为这样往往不会有好的结果。 费了这么多口舌就是希望把问题尽量的简单化。终于可以回到程序了那线程的情况是不是类似的呢答案是肯定的。 一个线程准备好后经过CPU的调度就可以自主的运行了。此时它俨然成了一个独立的个体且具有主观能动性。 这本是一件好事但却也有不好的一面那就是你对它的“掌控”能力变弱了颇有一种“将在外君命有所不受”的感觉。 可能你不同意这种看法说我可以“强迫”它停止运行调用Thread类的stop()方法来直接把它“掐死”不好意思该方法已废弃。 因为线程可能在运行一些“关键”代码比如转账此刻不能被终止。Thread类还有一些其它的方法也都废弃了大抵原因其实都差不多。 讲了这么多相信你已经明白了简单总结一下事情起因线程可以独立自主的运行可以认为它具有主观能动性。造成结果对它的掌控能力变弱了而且又不能直接把它“干掉”。解决方案凡事商量着来互相配合着把事情完成。作者观点其实就是把线程当作人来对待。
小试牛刀一下 一旦把线程当成人就来到了人类的世界这我们太熟悉了所以很多问题都会变得非常简单明了。一起来看看吧。 场景一停止 “大胖大胖12点了该去吃饭了别写了” “好的好的稍等片刻把这几行代码写完就走” 要点把停止的信号传达给别人别人处理完手头的事情就自己主动停止了。
static void stopByFlag() {ARunnable ar new ARunnable();new Thread(ar).start();ar.tellToStop();}static class ARunnable implements Runnable {volatile boolean stop;void tellToStop() {stop true;}Overridepublic void run() {println(进入不可停止区域 1。。。);doingLongTime(5);println(退出不可停止区域 1。。。);println(检测标志stop %s, String.valueOf(stop));if (stop) {println(停止执行);return;}println(进入不可停止区域 2。。。);doingLongTime(5);println(退出不可停止区域 2。。。);}}
解说线程在预设的地点检测flag来决定是否停止。
场景二暂停/恢复 “大胖大胖先别发请求了对方服务器快挂了” “好的好的等这个执行完就不发了” 过了一会 “大胖大胖可以重新发请求了” “好的好的” 要点把暂停的信号传达给别人别人处理完手头的事情就自己主动暂停了。但是恢复是无法自主进行的只能由操作系统来恢复线程的执行。 static void pauseByFlag() {BRunnable br new BRunnable();new Thread(br).start();br.tellToPause();sleep(8);br.tellToResume();}static class BRunnable implements Runnable {volatile boolean pause;void tellToPause() {pause true;}void tellToResume() {synchronized (this) {this.notify();}}Overridepublic void run() {println(进入不可暂停区域 1。。。);doingLongTime(5);println(退出不可暂停区域 1。。。);println(检测标志pause %s, String.valueOf(pause));if (pause) {println(暂停执行);try {synchronized (this) {this.wait();}} catch (InterruptedException e) {e.printStackTrace();}println(恢复执行);}println(进入不可暂停区域 2。。。);doingLongTime(5);println(退出不可暂停区域 2。。。);}}
解说还是在预设的地点检测flag。然后就是wait/notify配合使用。
场景三插队 “大胖大胖让我站到你前面不想排队了” “好吧” 要点别人插队到你前面必须等他完事后才轮到你。
static void jqByJoin() {CRunnable cr new CRunnable();Thread t new Thread(cr);t.start();sleep(1);try {t.join();} catch (InterruptedException e) {e.printStackTrace();}println(终于轮到我了);}static class CRunnable implements Runnable {Overridepublic void run() {println(进入不可暂停区域 1。。。);doingLongTime(5);println(退出不可暂停区域 1。。。);}}
解说join方法可以让某个线程插到自己前面等它执行完自己才会继续执行。 场景四叫醒 “大胖大胖醒醒醒醒看谁来了” “谁啊我去” 要点要把别人从睡梦中叫醒一定要采取稍微暴力一点的手段。
static void stopByInterrupt() {DRunnable dr new DRunnable();Thread t new Thread(dr);t.start();sleep(2);t.interrupt();}static class DRunnable implements Runnable {Overridepublic void run() {println(进入暂停。。。);try {sleep2(5);} catch (InterruptedException e) {println(收到中断异常。。。);println(做一些相关处理。。。);}println(继续执行或选择退出。。。);}}
解说线程在sleep或wait时是处于无法交互的状态的此时只能使用interrupt方法中断它线程会被激活并收到中断异常。
常见的协作配合 上面那些场景其实都是对一个线程的操作下面来看多线程间的一些配合。 事件一考试 假设今天考试20个学生1个监考老师。规定学生可以提前交卷即把卷子留下直接走人就行了。 但老师必须等到所有的学生都走后才可以收卷子然后装订打包。 如果把学生和老师都看作线程就是1个线程和20个线程的配合问题即等20个线程都结束了这1个线程才开始。 比如20个线程分别在计算数据等它们都结束后得到20个中间结果最后这1个线程再进行后续汇总、处理等。
static final int COUNT 20;public static void main(String[] args) throws Exception {new Thread(new Teacher(cdl)).start();sleep(1);for (int i 0; i COUNT; i) {new Thread(new Student(i, cdl)).start();}synchronized (ThreadCo1.class) {ThreadCo1.class.wait();}}static CountDownLatch cdl new CountDownLatch(COUNT);static class Teacher implements Runnable {CountDownLatch cdl;Teacher(CountDownLatch cdl) {this.cdl cdl;}Overridepublic void run() {println(老师发卷子。。。);try {cdl.await();} catch (InterruptedException e) {e.printStackTrace();}println(老师收卷子。。。);}}static class Student implements Runnable {CountDownLatch cdl;int num;Student(int num, CountDownLatch cdl) {this.num num;this.cdl cdl;}Overridepublic void run() {println(学生(%d)写卷子。。。, num);doingLongTime();println(学生(%d)交卷子。。。, num);cdl.countDown();}}
解说每完成一个线程计数器减1当减到0时被阻塞的线程自动执行。 事件二旅游 最近景色宜人公司组织去登山大伙都来到了山脚下登山过程自由进行。 但为了在特定的地点拍集体照规定1个小时后在半山腰集合谁最后到的要给大家表演一个节目。 然后继续登山在2个小时后在山顶集合拍照还是谁最后到的表演节目。 接着开始下山了在2个小时后在山脚下集合点名回家最后到的照例表演节目。 static final int COUNT 5;public static void main(String[] args) throws Exception {for (int i 0; i COUNT; i) {new Thread(new Staff(i, cb)).start();}synchronized (ThreadCo2.class) {ThreadCo2.class.wait();}}static CyclicBarrier cb new CyclicBarrier(COUNT, new Singer());static class Singer implements Runnable {Overridepublic void run() {println(为大家唱歌。。。);}}static class Staff implements Runnable {CyclicBarrier cb;int num;Staff(int num, CyclicBarrier cb) {this.num num;this.cb cb;}Overridepublic void run() {println(员工(%d)出发。。。, num);doingLongTime();println(员工(%d)到达地点一。。。, num);try {cb.await();} catch (Exception e) {e.printStackTrace();}println(员工(%d)再出发。。。, num);doingLongTime();println(员工(%d)到达地点二。。。, num);try {cb.await();} catch (Exception e) {e.printStackTrace();}println(员工(%d)再出发。。。, num);doingLongTime();println(员工(%d)到达地点三。。。, num);try {cb.await();} catch (Exception e) {e.printStackTrace();}println(员工(%d)结束。。。, num);}} 解说某个线程到达预设点时就在此等待等所有的线程都到达时大家再一起向下个预设点出发。如此循环反复下去。
事件三劳动 大胖和小白去了创业公司公司为了节约开支没有请专门的保洁人员。让员工自己扫地和擦桌。 大胖觉得擦桌轻松就让小白去扫地。可小白觉得扫地太累也想擦桌。 为了公平起见于是决定每人先干一半然后交换工具再接着干对方剩下的那一个半。
public static void main(String[] args) throws Exception {new Thread(new Staff(大胖, new Tool(笤帚, 扫地), ex)).start();new Thread(new Staff(小白, new Tool(抹布, 擦桌), ex)).start();synchronized (ThreadCo3.class) {ThreadCo3.class.wait();}}static ExchangerTool ex new Exchanger();static class Staff implements Runnable {String name;Tool tool;ExchangerTool ex;Staff(String name, Tool tool, ExchangerTool ex) {this.name name;this.tool tool;this.ex ex;}Overridepublic void run() {println(%s拿的工具是[%s]他开始[%s]。。。, name, tool.name, tool.work);doingLongTime();println(%s开始交换工具。。。, name);try {tool ex.exchange(tool);} catch (Exception e) {e.printStackTrace();}println(%s的工具变为[%s]他开始[%s]。。。, name, tool.name, tool.work);}}static class Tool {String name;String work;Tool(String name, String work) {this.name name;this.work work;}}
解说两个线程在预设点交换变量先到达的等待对方。
事件四魔性游戏 这是一个充满魔性的小游戏由一个团队一起参加。所有人每隔5秒钟抽一次签每个人有50%的概率留下来或被淘汰。 留下来的人下次抽签时同样有50%的概率被淘汰。被淘汰的人下次抽签时同样有50%的概率复活。 团队所有成员都被淘汰完为挑战失败团队所有成员都回到游戏中除刚开始外为挑战成功。 比如一开始10人参与游戏第一轮抽签后6人留下4人淘汰。 第二轮抽签后留下的6人中4人被淘汰淘汰的4人中2人复活那么目前是4人在游戏中6人被淘汰。 一直如此继续下去直到10人全部被淘汰或全部回到游戏中。 可见人数越多全部被淘汰的概率越小但全部回到游戏中的概率也越小。 反之人数越少全部回到游戏中的概率越大但全部被淘汰的概率也越大。 是不是很有魔性啊。哈哈。 static final int COUNT 6;public static void main(String[] args) throws Exception {new Thread(new Challenger(张三)).start();new Thread(new Challenger(李四)).start();new Thread(new Challenger(王五)).start();new Thread(new Challenger(赵六)).start();new Thread(new Challenger(大胖)).start();new Thread(new Challenger(小白)).start();synchronized (ThreadCo4.class) {ThreadCo4.class.wait();}}static Phaser ph new Phaser() {protected boolean onAdvance(int phase, int registeredParties) {println2(第(%d)局剩余[%d]人, phase, registeredParties);return registeredParties 0 ||(phase ! 0 registeredParties COUNT);};};static class Challenger implements Runnable {String name;int state;Challenger(String name) {this.name name;this.state 0;}Overridepublic void run() {println([%s]开始挑战。。。, name);ph.register();int phase 0;int h;while (!ph.isTerminated() phase 100) {doingLongTime(5);if (state 0) {if (Decide.goon()) {h ph.arriveAndAwaitAdvance();if (h 0)println(No%d.[%s]继续但已胜利。。。, phase, name);elseprintln(No%d.[%s]继续at(%d)。。。, phase, name, h);} else {state -1;h ph.arriveAndDeregister();println(No%d.[%s]退出at(%d)。。。, phase, name, h);}} else {if (Decide.revive()) {state 0;h ph.register();if (h 0)println(No%d.[%s]复活但已失败。。。, phase, name);elseprintln(No%d.[%s]复活at(%d)。。。, phase, name, h);} else {println(No%d.[%s]没有复活。。。, phase, name);}}phase;}if (state 0) {ph.arriveAndDeregister();}println([%s]结束。。。, name);}}static class Decide {static boolean goon() {return random(9) 4;}static boolean revive() {return random(9) 5;}} 解说某个线程到达预设点后可以选择等待同伴或自己退出等大家都到达后再一起向下一个预设点出发随时都可以有新的线程加入退出的也可以再次加入。
生产与销售的问题 在现实中工厂生产出来的产品会先放到仓库存储销售人员签了单子后会从仓库把产品发给客户。 如果生产的过快仓库里产品越堆越多直到把仓库堆满那就必须停止生产因为没地方放了。 此时只能让销售人员赶紧出去签单子把产品发出去仓库就有了空间可以恢复生产了。 如果销售的过快仓库里产品越来越少直到把仓库清空那就必须停止销售因为没产品了。 此时只能让生产人员赶紧生产产品把产品放到仓库里仓库里就有了产品可以恢复销售了。 可能会有人问为什么不让生产和销售直接挂钩呢把仓库这个环节去掉 这样会造成两种不好的情况 一是突然来了很多单子生产人员累成死Dog也生产不出来。 二是很长时间没有单子生产人员闲成废Dog也无事可做。 用稍微“专业”点的术语就是此时的生产和销售是一种强耦合的关系销售的波动对生产影响太大。 仓库就是一个缓冲区能有效的吸收波动很大程度上减少波动的传递起到一种解耦作用由强耦合变成一种松散耦合。 这其实就对应计算机里经典的生产者和消费者问题。经典的生产者和消费者 一到多个线程充当生产者生产元素。一到多个线程充当消费者消费元素。 在两者之间插入一个队列Queue充当缓冲区建立起生产者和消费者的松散耦合。 正常情况下即生产元素的速度和消费元素的速度差不多时生产者和消费者其实是不需要去关注对方的。 生产者可以一直生产因为队列里总是有空间。消费者可以一直消费因为队列里总是有元素。即达到一个动态的平衡。 但在特殊情况下比如生产元素的速度很快队列里没有了空间此时生产者必须自我“ba工”开始“睡大觉”。 一旦消费者消费了元素之后队列里才会有空间生产者才可以重启生产所以消费者在消费完元素后有义务去叫醒生产者复工。 更准确的说法应该是只有在生产者“睡大觉”时消费者消费完元素后才需要去叫醒生产者。否则其实可以不用叫醒因为人家本来就没睡。 反之如果消费元素的速度很快队列里没有了元素只需把上述情况颠倒过来即可。 但这样的话就会引入一个新的问题就是要能够准备的判断出对方有没有在睡大觉为此就必须定义一个状态变量在自己即将开始睡大觉时自己设置下这个变量。 对方通过检测这个变量来决定是否进行叫醒操作。当自己被叫醒后首先要做的就是清除一下这个变量表明我已经醒来复工了。 这样就需要多维护一个变量和多了一部分判断逻辑。可能有些人会觉得可以通过判断队列的“空”或“满”即队列中的元素数目来决定是否进行叫醒操作。 在高并发下可能刚刚判断队列不为空瞬间之后队列可能已经变为空的了这样会导致逻辑出错。线程可能永远无法被叫醒。 因此综合所有生产者每生产一个元素后都会通知消费者“现在有元素的你可以消费”。 同样消费者每消费一个元素后也会通知生产者“现在有空间的你可以生产”。 很明显这些通知很多时候即对方没有睡大觉时是没有真正意义的不过无所谓只要忽略它们就行了。 就是“宁可错杀一千也不放过一个”。首先要保证是正确的然后才有资格去BB别的。
public static void main(String[] args) {Queue queue new Queue();new Thread(new Producer(queue)).start();new Thread(new Producer(queue)).start();new Thread(new Consumer(queue)).start();}static class Producer implements Runnable {Queue queue;Producer(Queue queue) {this.queue queue;}Overridepublic void run() {try {for (int i 0; i 10000; i) {doingLongTime();queue.putEle(random(10000));}} catch (Exception e) {e.printStackTrace();}}}static class Consumer implements Runnable {Queue queue;Consumer(Queue queue) {this.queue queue;}Overridepublic void run() {try {for (int i 0; i 10000; i) {doingLongTime();queue.takeEle();}} catch (Exception e) {e.printStackTrace();}}}static class Queue {Lock lock new ReentrantLock();Condition prodCond lock.newCondition();Condition consCond lock.newCondition();final int CAPACITY 10;Object[] container new Object[CAPACITY];int count 0;int putIndex 0;int takeIndex 0;public void putEle(Object ele) throws InterruptedException {try {lock.lock();while (count CAPACITY) {println(队列已满%d生产者开始睡大觉。。。, count);prodCond.await();}container[putIndex] ele;println(生产元素%d, ele);putIndex;if (putIndex CAPACITY) {putIndex 0;}count;println(通知消费者去消费。。。);consCond.signalAll();} finally {lock.unlock();}}
public Object takeEle() throws InterruptedException { try {lock.lock();while (count 0) {println(队列已空%d消费者开始睡大觉。。。, count);consCond.await();}Object ele container[takeIndex];println(消费元素%d, ele);takeIndex;if (takeIndex CAPACITY) {takeIndex 0;}count--;println(通知生产者去生产。。。);prodCond.signalAll();return ele;} finally {lock.unlock();}}}
解说其实就是对await/signalAll的应用几乎面试必问。
阅读目录置顶)(长期更新计算机领域知识https://blog.csdn.net/weixin_43392489/article/details/102380691
阅读目录置顶)(长期更新计算机领域知识https://blog.csdn.net/weixin_43392489/article/details/102380882
阅读目录置顶)(长期科技领域知识https://blog.csdn.net/weixin_43392489/article/details/102600114