八种控制线程顺序的方法
作者:互联网
各位看官,我去年路过陈家村时,听到大神们在讨论一些排序算法,比如猴子排序法、睡眠排序法等,猴子排序法就是给猴子一堆乱序的数,
让它自己玩,最后总有一个顺序是对的!睡眠排序法,按数的大小分配线程睡眠时间,数越大睡眠时间就越长,然后同时启动全部线程,按
先后输出排序即成!想想也不无道理,那我就展开说说睡眠排序法,如何玩转线程执行顺序控制。
准备:
Idea2019.03/Gradle6.0.1/JDK11.0.4
难度: 新手--战士--老兵--大师
目标:
- 实现八种控制线程顺序的方法
步骤:
为了遇见各种问题,同时保持时效性,我尽量使用最新的软件版本。代码地址:本次无
第一招:线程配合join
publicclass ThreadJoinDemo { public static void main(String[] args) { final Thread thread1 = new Thread( ()->{ System.out.println("先买菜"); } ); final Thread thread2 = new Thread( ()->{ try { thread1.join(); //Waits for this thread to die. } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("再煎蛋"); } ); final Thread thread3 = new Thread( ()->{ try { thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("后吃饭"); } ); thread3.start(); thread1.start(); thread2.start(); } }
解析:调用thread.join()方法,当前线程将等待被join线程执行结束
第二招:主线程配合join
publicclass ThreadJoinDemo2 { public static void main(String[] args) throws InterruptedException { final Thread thread1 = new Thread( ()->{ System.out.println("先买菜"); } ); final Thread thread2 = new Thread( ()->{ System.out.println("再煎蛋"); } ); final Thread thread3 = new Thread( ()->{ System.out.println("后吃饭"); } ); thread1.start(); thread1.join(); thread2.start(); thread2.join(); thread3.start(); } }
解析:同上
第三招:synchronized锁,配合锁的wait/siganl唤醒机制
publicclass ThreadJoinDemo3 { privatestaticfinalbyte[] myLock1 = newbyte[0]; privatestaticfinalbyte[] myLock2 = newbyte[0]; privatestatic Boolean t1Run = false; privatestatic Boolean t2Run = false; public static void main(String[] args) { final Thread thread1 = new Thread( ()->{ synchronized (myLock1){ System.out.println("先买车"); t1Run = true; myLock1.notifyAll(); } } ); final Thread thread2 = new Thread( ()->{ synchronized (myLock1){ try { if (!t1Run){ System.out.println("买菜路上。。。"); myLock1.wait(); } synchronized (myLock2){ t2Run = true; System.out.println("后吃饭"); myLock2.notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } } ); final Thread thread3 = new Thread( ()->{ synchronized (myLock2){ try { if (!t2Run){ System.out.println("煎蛋糊了。。。"); myLock2.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("后吃饭"); myLock2.notifyAll(); } } ); thread3.start(); thread2.start(); thread1.start(); } }
解析:执行线程前,先去尝试获取锁,如果获取失败,就进入等待状态,
注意 1.加了两个状态变量的作用:如果thread1先运行完了,thread2才运行,thread2在等待thread1唤醒,这将导致thread2永远等待,
因为wait将使得当前线程进入等待直到被唤醒,2.使用空的byte[]数组做锁对象,为啥?因为体积小,效率高啊!
第四招:newSingleThreadExecutor线程池
publicclass ThreadJoinDemo4 { public static void main(String[] args) { final Thread thread1 = new Thread( ()->{ System.out.println("先买菜"); } ); final Thread thread2 = new Thread( ()->{ System.out.println("再煎蛋"); } ); final Thread thread3 = new Thread( ()->{ System.out.println("后吃饭"); } ); ExecutorService threadPool = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory()); threadPool.submit(thread1); threadPool.submit(thread2); threadPool.submit(thread3); // 关闭线程池:生产环境请注释掉,请君思考为啥? threadPool.shutdown(); } }
解析:newSingleThreadExecutor线程池对象中只有一个线程来执行任务,就会按照接收的任务顺序执行,只需按序提交任务即可。
第五招:lock配合condition
publicclass ThreadJoinDemo5 { privatestaticfinal Lock lock = new ReentrantLock(); privatestaticfinal Condition condition1 = lock.newCondition(); privatestaticfinal Condition condition2 = lock.newCondition(); privatestatic Boolean t1Run = false; privatestatic Boolean t2Run = false; public static void main(String[] args) { final Thread thread1 = new Thread( ()->{ // 注意lock/tryLock的区别: lock是void,没获取到锁,则进入休眠,tryLock是返回Boolean,执行后立即返回true/false lock.lock(); System.out.println("先买菜"); condition1.signal(); t1Run = true; // 生产环境下这里最好使用try/finally确保unlock执行 lock.unlock(); } ); final Thread thread2 = new Thread( ()->{ lock.lock(); try { if (!t1Run){ // Causes the current thread to wait until it is signalled condition1.await(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("再煎蛋"); t2Run = true; condition2.signal(); lock.unlock(); } ); final Thread thread3 = new Thread( ()->{ lock.lock(); try { if (!t2Run){ condition2.await(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("后吃饭"); lock.unlock(); } ); thread3.start(); thread2.start(); thread1.start(); } }
解析:lock对象上创建两个condition,线程执行前先加锁,若不是预期顺序的线程启动,则在该condition上进行wait等待直到收到signal信号,
注意点:condition 和 lock 执行先后关系,Before waiting on the condition the lock must be held by the current thread. await() will atomically
release the lock before waiting and re-acquire the lock before the wait returns.
第六招:CountDownLatch
publicclass ThreadJoinDemo6 { privatestatic CountDownLatch countDownLatch1 = new CountDownLatch(1); privatestatic CountDownLatch countDownLatch2 = new CountDownLatch(1); public static void main(String[] args) { final Thread thread1 = new Thread( ()->{ countDownLatch1.countDown(); System.out.println("先买菜"); } ); final Thread thread2 = new Thread( ()->{ try { // 注意这里不要写成 Object.wait() countDownLatch1.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("再煎蛋"); countDownLatch2.countDown(); } ); final Thread thread3 = new Thread( ()->{ try { countDownLatch2.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("后吃饭"); } ); thread3.start(); thread1.start(); thread2.start(); } }
解析:CountDownLatch即“倒计数”,只有计数变为零时,参与者才能执行,我们设置两个倒计数器,都置为1,非预期顺序的线程,必须等待计数归零。
第七招:Semaphore信号量法
publicclass ThreadJoinDemo7 { privatestatic Semaphore semaphore1 = new Semaphore(0); privatestatic Semaphore semaphore2 = new Semaphore(0); public static void main(String[] args) { final Thread thread1 = new Thread( ()->{ semaphore1.release(); System.out.println("先买菜"); } ); final Thread thread2 = new Thread( ()->{ try { semaphore1.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("再煎蛋"); semaphore2.release(); } ); final Thread thread3 = new Thread( ()->{ try { semaphore2.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("后吃饭"); } ); thread1.start(); thread3.start(); thread2.start(); } }
解析:Semaphore信号量对象,可以存放N个信号量,可以原子性的释放和请求这N个信号量,我们先预设两个存放0个信号量的对象,
非预期顺序的线程启动后,无法获取到信号量,进入等待,直到前序线程释放信号量。
注意:Semaphore中可以为负值,这时候,就必须确保release发生在acquire前面,比如Semaphore(0)和Semaphore(-1)的情况:
Semaphore(-1)的release可以,require则进入休眠,Semaphore(0)的release可以,require则进入休眠,即只有permit大于0时,才能require成功!
第八招:终极大法,睡眠法!
略,留个家庭作业!
后记:
- lambda表达式,如果看官还觉得我这个线程建立的写法不太爽,那就落伍啦,别再用下面的写法了,如果使用Idea,则会自动提示转为lambda表达式:
-
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("写成这样,表示您落伍了!"); } });
- CyclicBarrier(回环栅栏),我想了下,这个对象不适合控制顺序,只适合线程相互等待,然后一起运行,比如我们约好今天一起去吃大餐,集合后,至于谁先迈出出发的第一步,这个没法控制,故舍弃不用,
全文完!
我的其他文章:
- 1 移动应用APP购物车(店铺系列二)
- 2 H5开发移动应用APP(店铺系列一)
- 3 阿里云平台OSS对象存储
- 4 Dubbo学习系列之十七(微服务Soul网关)
- 5 Docker部署RocketMQ
只写原创,敬请关注
标签:顺序,Thread,System,八种,控制线,println,new,final,out 来源: https://www.cnblogs.com/xxbiao/p/12522667.html