zoukankan      html  css  js  c++  java
  • 如何保证线程的顺序执行

    问题:

    现在有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开发,越学习越感觉知识太多,自身了解太少,只能不断追寻
  • 相关阅读:
    Oracle SQL语句大全—查看表空间
    Class to disable copy and assign constructor
    在moss上自己总结了点小经验。。高手可以飘过 转贴
    在MOSS中直接嵌入ASP.NET Page zt
    Project Web Access 2007自定义FORM验证登录实现 zt
    SharePoint Portal Server 2003 中的单一登录 zt
    vs2008 开发 MOSS 顺序工作流
    VS2008开发MOSS工作流几个需要注意的地方
    向MOSS页面中添加服务器端代码的另外一种方式 zt
    状态机工作流的 SpecialPermissions
  • 原文地址:https://www.cnblogs.com/fengtingxin/p/13855663.html
Copyright © 2011-2022 走看看