zoukankan      html  css  js  c++  java
  • 八种控制线程顺序的方法

    各位看官,我去年路过陈家村时,听到大神们在讨论一些排序算法,比如猴子排序法、睡眠排序法等,猴子排序法就是给猴子一堆乱序的数,

    让它自己玩,最后总有一个顺序是对的!睡眠排序法,按数的大小分配线程睡眠时间,数越大睡眠时间就越长,然后同时启动全部线程,按

    先后输出排序即成!想想也不无道理,那我就展开说说睡眠排序法,如何玩转线程执行顺序控制。

    作者原创文章,谢绝一切转载!

    本文只发表在"公众号"和"博客园",其他均属复制粘贴!如果觉得排版不清晰,请查看公众号文章。 

    准备:

    Idea2019.03/Gradle6.0.1/JDK11.0.4

    难度 新手--战士--老兵--大师

    目标:

    1. 实现八种控制线程顺序的方法

    步骤:

    为了遇见各种问题,同时保持时效性,我尽量使用最新的软件版本。代码地址:本次无

     

    第一招:线程配合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成功!

     

    第八招:终极大法,睡眠法!

    略,留个家庭作业!


     

    后记:

    1. lambda表达式,如果看官还觉得我这个线程建立的写法不太爽,那就落伍啦,别再用下面的写法了,如果使用Idea,则会自动提示转为lambda表达式:
    2. Thread thread = new Thread(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("写成这样,表示您落伍了!");
                  }
              });
    3. CyclicBarrier(回环栅栏),我想了下,这个对象不适合控制顺序,只适合线程相互等待,然后一起运行,比如我们约好今天一起去吃大餐,集合后,至于谁先迈出出发的第一步,这个没法控制,故舍弃不用,

    全文完!

     


    我的其他文章:

    只写原创,敬请关注

  • 相关阅读:
    SELinux
    Horovod
    kubeflow
    k8s Custom Resource
    k8s Service
    k8s Deployment
    k8s ReplicaSet
    BytePS源码解析
    突破传统 OJ 瓶颈,“判题姬”接入云函数
    前端如何真正晋级成全栈:腾讯 Serverless 前端落地与实践
  • 原文地址:https://www.cnblogs.com/xxbiao/p/12522667.html
Copyright © 2011-2022 走看看