让线程乖乖~按顺序执行

最近总是遇到这样的问题:怎么控制线程访问顺序?怎么让线程B在线程A执行之后再执行?怎么让线程A、B、C按顺序打印ABCABC?     以上都可以归结为线程的顺序执行问题,有这么4种方案:

(1)join():“等你执行结束,我再执行”

(2)singleThreadPool:只有一个线程的线程池,任务乖乖在队列中等待被执行

(3)wait/notify机制:“兄弟们,醒醒,到你了”

(4)ReentrantLock 的 condition 的 await/signal机制:“那个兄弟,醒醒,到你了”

接下来就这四种方式进行详细说明

 

一、Join()

1、简介:join(),等待线程结束,才能继执行。

2、本质:让线程wait在当前线程对象实例上,观察join的源码:

    public final void join() throws InterruptedException {
        join(0);
    }
public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        //---------重点-------------
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        //--------------------------
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

解读:阻塞当前线程,直到目标线程运行结束,如果参数是0,意味着无限等待。

3、join实现线程顺序执行

(1)方式1:在run()中调用join方法。

public class M1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> System.out.println("A"));
        Thread t2 = new Thread(() -> {
            try {
                t1.join();  //瞧,线程2等待线程1执行结束,然后再执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B");});
        Thread t3 = new Thread(() -> {
            try {
                t2.join();  //线程3等待线程2执行结束,然后再执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("C");});
        t1.start();
        t3.start();
        t2.start();
        //输出:A B C
    }
}

(2)方式2:在主方法中调用join

public class M2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> System.out.println("A"));
        Thread t2 = new Thread(() -> System.out.println("B"));
        Thread t3 = new Thread(() -> System.out.println("C"));
        t1.start();
        t1.join();
        t2.start();
        t2.join();
        t3.start();
        //输出:A  B  C
    }
}

 

二、单线程线程池

1、简介:只有一个线程的线程池,任务都在等待队列中排队,所以它们是一个一个执行的

2、两种创建方式:

ExecutorService single=new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
ExecutorService single = Executors.newSingleThreadExecutor();

3、单线程线程池实现顺序执行

public class M2 {
    public static void main(String[] args){
        Thread t5=new Thread(()->System.out.println("A"));
        Thread t6=new Thread(()->System.out.println("B"));
        Thread t7=new Thread(()->System.out.println("C"));
        ExecutorService single=new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        single.submit(t5);
        single.submit(t7);
        single.submit(t6);
    }
}

 

三、wait/notify机制

1、简介:有多种实现方式,这里介绍通过一个变量的取值来控制线程执行顺序。

      注意:wait/notify 要求获得对象的monitor,所以只能在同步方法或同步代码块中使用

2、wait/notify实现线程顺序执行

  • 先创建一个类,类中包含一个变量,flag。我们约定,flag=0时,输出A,flag=1时,输出B,flag=2,输出C
class MyThread {
    private static int flag=0;
    //str:稍后要打印的值;order:取值范围[0,1,2],分别代表先后顺序
    public synchronized void printStr(String str,int order){
        while (flag!=order){
            try{
                this.wait(); //否则释放锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(str);
        if (order<2)
            flag++;
        else
            flag=1;
        this.notifyAll(); //唤起其他线程
    }
}
  • 主方法中调用
public class M3 {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t8 = new Thread(new Runnable() {
            @Override
            public void run() {
                myThread.printStr("A", 0); //第一个,打印A
            }
        });
        Thread t9 = new Thread(new Runnable() {
            @Override
            public void run() {
                myThread.printStr("B", 2); //第3,打印B
            }
        });
        Thread t10 = new Thread(new Runnable() {
            @Override
            public void run() {
                myThread.printStr("C", 1); //第二,打印C
            }
        });
        t8.start();
        t9.start();
        t10.start();
        //执行结果:A    C     B
    }
}

 

 

四、condition的await/signal机制

1、简介:这个机制和wait/notify很像,(笔者狭隘地认为,condition一定情况下可以更加精确~)

2、Conditon:await/signal要配合ReentrantLock使用,通过ReentrantLock的newConditon方法生成一个与当前重入锁绑定的Condition实例,让我们能在线程何时的时间等待,或在一个特定的时刻获得通知。阻塞队列之所以能够阻塞,底层也是利用了condition哦

3、await/signal实现线程顺序执行

public class M5{
    private static Lock lock=new ReentrantLock();
    private static Condition condition1=lock.newCondition();
    private static Condition condition2=lock.newCondition();
    private static boolean printA=true;  //先打印A
    private static boolean printB=false; //先打印B
    public static void main(String[] args) throws InterruptedException {
        Thread t11=new Thread(() -> {
            try{
                lock.lock();
                if (!printA) {
                   condition1.await(); //如果当前printA=false,则在条件1上等待
                }
                System.out.println("A");
                printB=true;
                printA=false;
                condition2.signal(); //唤醒在condition2上等待的线程
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });
        Thread t12=new Thread(() -> {
            try{
                lock.lock();
                if (!printB){
                    condition2.await(); 
                }
                System.out.println("B");
                printA=true;
                printB=false;
                condition1.signal();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });
        t11.start();
        t12.start();
    }

}

 

参考资料:

1、《Java高并发程序设计》

2、让线程按顺序执行8种方法:https://www.cnblogs.com/wenjunwei/p/10573289.html

 

 

 

你可能感兴趣的:(java,后台,多线程)