多线程: 如何实现多个线程按序打印?

题目

提供一个类

public class Foo {
    private static void printA() {
                System.out.print("A");
    }
    private static void printB() {
                System.out.print("B");
    }
    private static void printC() {
                System.out.print("C");
    }

会有3个线程分别调用printA(),printB(),printC()方法;
编程实现输出结果是ABC.

透过问题看本质:

要实现按序打印,也就是printB要在printA执行之后执行, printC要在printB执行之后执行;

1.利用wait()和notifyAll()

    private static final Object obj = new Object();
    private static int type = 1;

    private static void printA() {
        synchronized (obj) {

            if (type == 1) {
                System.out.print("A");
                type = 2;
                //更新type后唤醒其他线程
                obj.notifyAll();
            }
        }
    }

    private static void printB() throws InterruptedException {
        synchronized (obj) {

            while (type != 2) {
                //当type!=2时,阻塞当前线程
                obj.wait();
            }
            if (type == 2) {
                System.out.print("B");
                type = 3;
                //更新type后唤醒其他线程
                obj.notifyAll();
            }
        }
    }

    private static void printC() throws InterruptedException {
        synchronized (obj) {

            while (type != 3) {
                //当type!=3时,阻塞当前线程
                obj.wait();
            }
            if (type == 3) {
                System.out.print("C");
            }
        }
    }

    @Test
    public void test1() throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                printA();
            }
        }).start();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                printB();
            }
        }).start();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                printC();
            }
        }).start();
        Thread.sleep(3000);
    }

思路:

当线程A没执行之前,利用wait()方法阻塞B, C线程; 线程A执行完成之后,更新type值并利用notifyAll()唤醒B,C线程, 使得B线程得以往下执行(此时C线程被唤醒后因为type !=3,因此会被wait()方法再次阻塞);B线程执行完成后更新type值并唤醒线程C,使得线程C得以往下执行;

2. 利用CountDownLatch

//初始化两个值为1的计数器
    private static CountDownLatch countDownLatchB = new CountDownLatch(1);
    private static CountDownLatch countDownLatchC = new CountDownLatch(1);
    private static void printA2() {
        System.out.print("A");
        countDownLatchB.countDown();//将计数器值减1
    }

    private static void printB2() throws InterruptedException {
        countDownLatchB.await();//当值不为0时,当前线程会被阻塞
        System.out.print("B");
        countDownLatchC.countDown();
    }

    private static void printC2() throws InterruptedException {
        countDownLatchC.await();
        System.out.print("C");
    }

    @Test
    public void test2() throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                printA2();
            }
        }).start();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                printB2();
            }
        }).start();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                printC2();
            }
        }).start();
        Thread.sleep(3000);
    }

CountDownLatch:

CountDownLatch是一个计数器, 它在初始化时要输入一个数值型的参数作为计数器的值; 其有两个主要的方法,await()和countDown(); countDown()方法会将计数器的值减1;await()方法是:当计数器值不为0时,阻塞当前线程;计数器值为0时,唤醒当前线程; 该篇文章有对CountDownLatch的详细解析:如何实现某个线程在其他线程执行完毕之后再执行

思路:

利用CountDownLatch,创建两个值为1的计数器;分别用来阻塞B,C线程;当线程A执行完成之后,将B线程的计数器减1,使其计数器值为0,从而B线程得以唤醒;同理,B线程执行完成之后会唤醒C线程;从而达到按序执行的效果

你可能感兴趣的:(编程题,并发编程)