• 如何保证线程的顺序执行


    问题:

    现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完之后执行,T3在T2执行完之后执行?

    方法1:使用join

    使用Thread原生方法join,join方法是使所属的线程对象x正常执行run()方法中的任务,而当前线程进行无限的阻塞,等到线程x执行完成后再继续执行当前线程后面的代码。

    public static void main(String[] args) {
            final Thread T1 = new Thread(new Runnable() {
                public void run() {
                    System.out.println("T1 run");
                }
            });
            final Thread T2 = new Thread(new Runnable() {
                public void run() {
                    System.out.println("T2 run");
                    try{
                        T1.join();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T2 run end");
                }
            });
            Thread T3 = new Thread(new Runnable() {
                public void run() {
                    System.out.println("T3 run");
                    try{
                        T2.join();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T3 run end");
                }
            });
            T1.start();
            T2.start();
            T3.start();
    
        }

     

    方法2:使用线程间通信的等待/通知机制

    wait()方法是Object类的方法,该方法用来将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。

    在调用wait之前,线程必须获取到该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前线程释放锁。

    private static boolean T2Run = false; //标识位,用来通知T2线程执行
    
        private static boolean T3Run = false;
    
    
        public static void main(String[] args) {
    
            Object lock1 = new Object();
            Object lock2 = new Object();
    
            Thread T1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock1){
                        System.out.println("T1 run");
                        //t1 线程通知t2执行
                        T2Run = true;
                        lock1.notify();
                    }
                }
            });
            Thread T2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock1){
                        if(!T2Run){
                            System.out.println("T2 wait");
                            try {
                                lock1.wait();
                            } catch (Exception e){
                                e.printStackTrace();
                            }
                        }
                        System.out.println("T2 run");
                        //t2 线程通知t3执行
                        synchronized (lock2){
                            T3Run = true;
                            lock2.notify();
    
                        }
                    }
                }
            });
    
            Thread T3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock2){
                        if (!T3Run){
                            System.out.println("T3 wait");
                            try {
                                lock2.wait();
                            } catch (Exception e){
                                e.printStackTrace();
                            }
                        }
                        System.out.println("T3 run");
                    }
                }
            });
            T1.start();
            T2.start();
            T3.start();
        }

     

    方法3:使用Conditon

    关键字synchronized与wait和notify/notifyAll方法相结合可以实现等待/通知模式,类ReetrantLock也可以实现同样的功能,但需要借助于Condition对象。Condition可以实现多路通知,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。在使用notify/notifyAll通知时,被通知的线程却是由JVM随机选择的。

    /**
         * 使用condition
         */
        private Lock lock = new ReentrantLock();
    
        private Condition condition2 = lock.newCondition();
    
        private Condition condition3 = lock.newCondition();
    
        private static Boolean t2Run = false;
        private static Boolean t3Run = false;
    
        private void useCondition(){
            Thread T1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock(); //获取锁
                    System.out.println("T1 run");
                    t2Run = true; //设置t2可以运行
                    System.out.println("T1 run finish signal T2");
                    condition2.signal(); //通知T2执行
                    lock.unlock(); //解锁当前线程
                    System.out.println("T1 unlock");
                }
            });
            Thread T2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try{
                        if (!t2Run){
                            condition2.await(); //如果是false ,则等待
                        }
                        //若是true,则代表T2可以执行
                        System.out.println("T2 run");
                        t3Run = true;
                        condition3.signal();
                        System.out.println("T2 run finish signal T3");
                    }catch (Exception e){
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
    
                }
            });
    
            Thread T3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try{
                        if (!t3Run){
                            condition3.await(); //如果是false ,则等待
                        }
                        //若是true,则代表T2可以执行
                        System.out.println("T3 run");
                    }catch (Exception e){
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            T1.start();
            T2.start();
            T3.start();
        }

    方法4:使用线程池

    使用newSingleThreadExecutor线程池,由于核心线程数只有一个,所以能够顺序执行。

    /**
         * 线程池
         * 核心线程数:1
         * 最大线程数:1
         * 在日常中不建议使用newSingleThreadExecutor,因为阻塞队列个数没有限制,会导致内存溢出
         *
         */
        static ExecutorService executorService = Executors.newSingleThreadExecutor();
    
    
        public static void main(String[] args) {
            Thread T1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("T1 run");
                }
            });
            Thread T2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("T2 run");
                }
            });
            Thread T3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("T3 run");
                }
            });
            executorService.submit(T1);
            executorService.submit(T2);
            executorService.submit(T3);
            executorService.shutdown();
        }

     

    方法5:使用线程的CountDownLatch

    CountDownLatch 的作用是:当一个线程需要另外一个或多个线程完成后,再开始执行。比如主线程要等待一个子线程完成环境相关配置的加载工作,主线程才继续执行,就可以利用 CountDownLatch 来实现。

    比较重要的方法:

    CountDownLatch(int count); //构造方法,创建一个值为count 的计数器

    await();//阻塞当前线程,将当前线程加入阻塞队列。

    countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。

    /**
         * 计数器1 用于T1线程通知T2线程
         * 注意:这里个数都设置成立1 ,当T1执行完成后,执行countDown,来通知T2线程
         */
        static CountDownLatch countDownLatch1 = new CountDownLatch(1);
    
        /**
         * 计数器2 用于T2线程通知T3线程
         */
        static CountDownLatch countDownLatch2 = new CountDownLatch(1);
    
        public static void main(String[] args) {
            Thread T1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("T1 run");
                    countDownLatch1.countDown();
                    System.out.println("T1 countDown finish");
                }
            });
            Thread T2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        countDownLatch1.await();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T2 run");
                    countDownLatch2.countDown();
                }
            });
            Thread T3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        countDownLatch2.await();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T3 run");
                }
            });
        }

    方法6:使用cyclicbarrier (多个线程互相等待,直到到达同一个同步点,再继续一起执行

    比较重要的方法 

    CyclicBarrier(int parties) //构造方法,参数表示拦截的线程的个数

    CyclicBarrier(int parties, Runnable barrierAction) //也是构造方法,可以通过后面的参数,这是线程的优先级

    await() //告诉CyclicBarrier自己已经到达同步点,然后当前线程被阻塞,当所有线程都到达同步点(barrier)时,唤醒所有的等待线程,一起往下继续运行,可根据参数barrierAction决定优先执行的线程

    /**
         * 设置2个线程互相等待,直到到达同一个同步点,再继续一起执行。T1不执行完,T2就永远不会执行
         */
        static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
    
        /**
         * 设置2个线程互相等待,直到到达同一个同步点,再继续一起执行。
         */
        static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
    
        public static void main(String[] args) {
            Thread T1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("T1 run");
                    try{
                        cyclicBarrier1.await();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });
            Thread T2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        cyclicBarrier1.await();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T2 run");
                    try{
                        cyclicBarrier2.await();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });
            Thread T3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        cyclicBarrier2.await();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T3 run");
                }
            });
    
            T1.start();;
            T2.start();
            T3.start();
        }

    方法7:使用信号量 Semaphore

    Semaphore计数信号量,常用于限制可以访问某些资源(物理或逻辑的)线程数目。

    常用的方法:

    Semaphore(int permits);//构造方法,permits就是允许同时运行的线程数目

    public Semaphore(int permits,boolean fair);//permits就是允许同时运行的线程数目 ,fair 是否为公平锁,如果是公平锁,那么获得锁的顺序与线程启动顺序有关

    void acquire()// 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。

    tryAcquire() //尝试获得令牌,返回获取令牌成功或失败,不阻塞线程

    release() //释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。

    /**
         * 设置信号量初始值为0 ,让T1 把信号量+1,这样,T2就可以执行了
         */
        static Semaphore semaphore1 = new Semaphore(0);
    
        static Semaphore semaphore2 = new Semaphore(0);
    
        public static void main(String[] args) {
            Thread T1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("T1 run");
                    try{
                        semaphore1.release();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T1 semaphore1 + 1");
                }
            });
            Thread T2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        semaphore1.acquire();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T2 semaphore2 + 1");
                    try{
                        semaphore2.release();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T2 run");
                }
            });
            Thread T3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        semaphore2.acquire();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("T3 run");
                }
            });
    
            T1.start();;
            T2.start();
            T3.start();
        }

     总结:共有7中方法

    1. 使用Thread原生方法join

    2. 使用线程间通信的等待/通知机制

    3. 使用Conditon

    4. 使用线程池

    5. 使用线程的CountDownLatch

    6. 使用cyclicbarrier (多个线程互相等待,直到到达同一个同步点,再继续一起执行
    7. 使用信号量 Semaphore

     

    参考:

    https://www.cnblogs.com/wenjunwei/p/10573289.html

     

    一个入行不久的Java开发,越学习越感觉知识太多,自身了解太少,只能不断追寻
  • 相关阅读:
    数据库产生的背景
    VS2008执行MFC程序,提示microsoft incremental linker已停止工作解决方法
    leetcode第一刷_Add Binary
    【MongoDB】深入了解MongoDB不可不知的十点
    哈理工2015暑假训练赛 zoj 2078Phone Cell
    dpdk l2fwd 应用流程分析
    在Redhat Linux中执行非Redhat的Openstack, Redhat将对其Linux不提供支持
    Wing IDE 怎样设置 python版本号
    Shell编程入门
    通达OA 小飞鱼OA实施法:以项目管理的方式来推进工作流设计项目实施
  • 原文地址:https://www.cnblogs.com/fengtingxin/p/13855663.html
Copyright © 2020-2023  润新知