八种控制线程顺序的方法

2019/04/10 10:10
阅读数 12

 

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

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

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

准备:

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(回环栅栏),我想了下,这个对象不适合控制顺序,只适合线程相互等待,然后一起运行,比如我们约好今天一起去吃大餐,集合后,至于谁先迈出出发的第一步,这个没法控制,故舍弃不用,

全文完!

 


我的其他文章:

只写原创,敬请关注

原文出处:https://www.cnblogs.com/xxbiao/p/12522667.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部