记录了如何控制多个线程的执行顺序,以练习题的形式记录

一、两个线程顺序执行

题目描述:

线程t1会打印A,线程t2会打印B,实现先打印B再打印A

题目分析:

实现的关键是线程1执行的时候要确定线程2已经执行过了,如果线程2还没执行就让线程1继续等待

wait notify 解法

public class Test7 {

    private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);

    private static boolean t2IsRun;
    private static Object lock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    //t2还没执行前让t1 先wait
                    while (!t2IsRun){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    //t2执行完了
                    LOGGER.info("A");
                }
            }
        },"t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    LOGGER.info("B");
                    //通知t1可以执行了
                    t2IsRun=true;
                    lock.notifyAll();
                }
            }
        },"t2");
        t1.start();
        t2.start();
    }
}

当然这里的lock对象可以换成ReentrantLock,这样就不用使用synchronized,可以在ReentrantLock的条件变量

Condition上进行等待,使用await和signal方法控制,道理是一样的。

Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // t2还没执行前让t1 先wait
                lock.lock();
                try {
                    while (!t2IsRun) {
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            return;
                        }
                    }
                } finally {
                    lock.unlock();
                }

                //t2执行完了
                LOGGER.info("A");
            }
        }, "t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    LOGGER.info("B");
                    //通知t1可以执行了
                    t2IsRun = true;
                    condition.signal();
                } finally {
                    lock.unlock();
                }
            }
        }, "t2");
        t1.start();
        t2.start();

park unpark解法

private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // t2还没执行前让t1先暂停
                LockSupport.park();
                //t2执行完了
                LOGGER.info("A");
            }
        }, "t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info("B");
                //通知t1可以执行了
                LockSupport.unpark(t1);
            }
        }, "t2");
        t1.start();
        t2.start();
    }

这种解法里利用了park unpark方法的特性,

如果t1先执行park方法,线程t1会暂停执行,进入Waiting状态,当t2执行了unpark方法后t1恢复执行进入Runnable状态。

如果t2先执行unpark方法t1后边再执行park方法,则t1执行park方法时不会进行等待,会继续执行。

这个原理可以用一个比喻来理解,线程里有一个存干粮的地方,而且只能存一份,默认是空的,没有干粮,

park方法就是用来检查当前有没有干粮,如果没有干粮,线程就会开始等待,啥时候干粮来了就把干粮吃掉然后继续向下执行,注意这时候干粮槽又空了。如果当前有干粮,线程就会把干粮吃掉然后继续执行也就是不会等待。

unpark方法就是补充干粮,所以就会产生一种效果: 先执行的unpark可以让后执行的park方法不暂停。

二、三个线程交替执行

题目描述:

线程t1打印A,线程t2打印B,线程t3打印C,实现A,B,C这样的打印,然后循环10次

题目分析:

控制两个线程的先后可以使用布尔变量,控制三个线程的先后顺序可以使用int a变量来存储3中状态

a=1 t1运行,a=2 t2运行, a=3 t3运行,如果不满足条件就等待,t1运行完后让a=2,t2运行完让a=3,t3运行完让

a=1,这样就实现了交替

wait notify解法

public class Test7 {

    private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);

    private static final Object lock = new Object();
    private static int a=1;

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    //锁放在里边是在每一次循环的时候加锁,循环体执行完就释放锁,后边它如果抢到锁了就
                    //会开始下一轮循环,但是因为a的状态不对又会wait然后释放锁
                    
                    //注意锁加在for循环外边也是可以的,因为t1执行完会改变标记a,a的值改变后下次循环
                    //就会wait然后释放锁,这样别的线程就能抢到锁了
                    synchronized (lock){
                        while (a!=1) {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                return;
                            }
                        }

                        LOGGER.info("A");
                        //改变标记 唤醒让下一个线程开始执行
                        a=2;
                        lock.notifyAll();
                    }
                }
            }
        },"t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    synchronized (lock){
                        while (a!=2){
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                return;
                            }
                        }

                        LOGGER.info("B");
                        //叫醒下一个线程
                        a=3;
                        lock.notifyAll();
                    }
                }
            }
        },"t2");

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    synchronized (lock){
                        while (a!=3){
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        LOGGER.info("C");
                        a=1;
                        lock.notifyAll();
                    }
                }
            }
        },"t3");

        t1.start();
        t2.start();
        t3.start();
    }
}

await和signal 解法

public class Test7 {

    private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);

    private static final ReentrantLock lock = new ReentrantLock();

    private static  Condition condition1 =lock.newCondition();
    private static  Condition condition2 =lock.newCondition();
    private static  Condition condition3 =lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    lock.lock();
                    try {
                        //先wait
                        condition1.await();
                        LOGGER.info("A");
                        //叫醒下一个condition的线程
                        condition2.signal();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return;
                    } finally {
                        lock.unlock();
                    }
                }
            }
        },"t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    lock.lock();
                    try {
                        condition2.await();
                        LOGGER.info("B");
                        //叫醒下一个condition的线程
                        condition3.signal();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return;
                    } finally {
                        lock.unlock();
                    }
                }
            }
        },"t1");

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    lock.lock();
                    try {
                        condition3.await();
                        LOGGER.info("C");
                        //叫醒下一个condition的线程
                        condition1.signal();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return;
                    } finally {
                        lock.unlock();
                    }
                }
            }
        },"t3");

        t1.start();
        t2.start();
        t3.start();
        //等待,确保三个线程都进入了等待
        Thread.sleep(1000);
        //主线程获取锁叫醒t1
        lock.lock();
        try {
            condition1.signal();
        } finally {
            lock.unlock();
        }
    }
}

准备三个Condition,一开始让三个线程都去condition等待,每个线程打印完后去唤醒下一个应该执行的线程,

等所有线程都运行后主线程中叫醒t1

park unpark解法

public class Test7 {

    private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);

    static Thread t1;
    static Thread t2;
    static Thread t3;

    public static void main(String[] args) throws InterruptedException {

        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    LockSupport.park();
                    LOGGER.info("A");
                    LockSupport.unpark(t2);
                }
            }
        },"t1");

        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    LockSupport.park();
                    LOGGER.info("B");
                    LockSupport.unpark(t3);
                }
            }
        },"t2");

        t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    LockSupport.park();
                    LOGGER.info("C");
                    LockSupport.unpark(t1);
                }
            }
        },"t3");

        t1.start();
        t2.start();
        t3.start();

        //利用unpark方法的特性,不用关是park先执行还是unpark先执行,叫醒t1
        LockSupport.unpark(t1);

    }
}

这种解法还是一开始让所有线程都暂停住,然后利用unpark方法的特性在主线程里让t1恢复执行