手记

多线程交替打印ABC

对于一些需要多线程保持顺序的场景,需要我们实现线程间的交互。
常见多线程通信方式:

  • Synchronized + wait() + notify()
  • Lock + await() + signal()
  • semaphore
  • 基于semaphore的变体

结合经典例题进行分析:

多线程打印10遍ABC,形如ABCABCABCABC
  1. Synchronized + wait() + notify()
    wait()与notify()作为Object类中的方法,作用如下

    wait() 持有锁的线程,释放锁,一直阻塞,直到有别的线程调用notify()将其唤醒
    notify() 通知一个等待线程,唤醒任意一个wait线程

private static int count = 30;

    static class ThreadA extends Thread {
        Thread c;

        public void setC(Thread c) {
            this.c = c;
        }

        @Override
        public void run() {
            while (count-- > 0) {
                synchronized (c) {
                    synchronized (this) {
                        System.out.print("A");
                        this.notify();
                    }

                    try {
                        c.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    static class ThreadB extends Thread {
        Thread a;

        public void setA(Thread a) {
            this.a = a;
        }

        @Override
        public void run() {
            while (count-- > 0) {
                synchronized (a) {
                    synchronized (this) {
                        System.out.print("B");
                        this.notify();
                    }

                    try {
                        a.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    static class ThreadC extends Thread {
        Thread b;

        public void setB(Thread b) {
            this.b = b;
        }

        @Override
        public void run() {
            while (count-- > 0) {
                synchronized (b) {
                    synchronized (this) {
                        System.out.print("C");
                        this.notify();
                    }

                    try {
                        b.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadA a = new ThreadA();
        ThreadB b = new ThreadB();
        ThreadC c = new ThreadC();

        a.setC(c);
        b.setA(a);
        c.setB(b);

        a.start();
        Thread.sleep(100);
        b.start();
        Thread.sleep(100);
        c.start();
    }
  1. Lock + await() + signal()

    await()与signal()方法有点类似wait(),notify()。不过await()与signal()是Condition工具类下的方法
    Condition在内部维护了一个队列,简言之,
    await() 将线程包装成节点并放入队列中,阻塞当前线程
    signal() 从同步队列中重新获取线程信息。

    static ReentrantLock lock = new ReentrantLock();
    static Condition conditionA = lock.newCondition();
    static Condition conditionB = lock.newCondition();
    static Condition conditionC = lock.newCondition();
    static String state = "A";

    static void nextState() {
        switch (state) {
            case "A":
                state = "B";
                break;
            case "B":
                state = "C";
                break;
            default:
                state = "A";
        }
    }

    static class PrintState extends Thread {
        String name;

        public PrintState(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            printState(getCurrentCondition(), getNextCondition());
        }

        void printState(Condition currentCondition, Condition nextCondition) {
            for (int i = 0; i < 10; i++) {
                lock.lock();
                if (!name.equals(state)) {
                    try {
                        currentCondition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(state);
                nextState();
                nextCondition.signal();
                lock.unlock();
            }
        }

        Condition getCurrentCondition() {
            switch (name) {
                case "A":
                    return conditionA;
                case "B":
                    return conditionB;
                default:
                    return conditionC;
            }
        }

        Condition getNextCondition() {
            switch (name) {
                case "A":
                    return conditionB;
                case "B":
                    return conditionC;
                default:
                    return conditionA;
            }
        }
    }

    public static void main(String[] args) {
        PrintState printStateA = new PrintState("A");
        PrintState printStateB = new PrintState("B");
        PrintState printStateC = new PrintState("C");
        printStateA.start();
        printStateC.start();
        printStateB.start();
    }
  1. semaphore
    信号量提供操作计数的方式来同步控制线程
    acquire() 信号量减1,若信号量为0,则阻塞
    release() 信号量加1
static Semaphore a = new Semaphore(1);
    static Semaphore b = new Semaphore(0);
    static Semaphore c = new Semaphore(0);

    private static class PrintABC extends Thread {
        String state;
        Semaphore currentSemaphore;
        Semaphore nextSemaphore;

        public PrintABC(String state) {
            this.state = state;
            switch (state) {
                case "A":
                    currentSemaphore = a;
                    nextSemaphore = b;
                    break;
                case "B":
                    currentSemaphore = b;
                    nextSemaphore = c;
                    break;
                default:
                    currentSemaphore = c;
                    nextSemaphore = a;
                    break;

            }
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    currentSemaphore.acquire();
                    System.out.print(state);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                nextSemaphore.release();
            }
        }
    }

    public static void main(String[] args) {
        PrintABC printA = new PrintABC("A");
        PrintABC printB = new PrintABC("B");
        PrintABC printC = new PrintABC("C");

        printA.start();
        printB.start();
        printC.start();

    }

4.基于semaphore的变体
通过上述实现方法,我们可以得知,多线程间通信的思路是由符合条件的线程获取CPU资源并执行,若不符合条件的线程获取到的CPU信息,则进行阻塞。
对于该例题,我们可以通过变量映射线程执行顺序。从而控制线程流转.

    private static AtomicInteger autoInt = new AtomicInteger();

    private static class PrintNum extends Thread {
        private int num;

        public PrintNum(int num) {
            this.num = num;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                if (autoInt.intValue() % 3 == num) {
                    printStr();
                    autoInt.intValue();
                }
                Thread.yield();
            }
        }

        private void printStr() {
            switch (num) {
                case 0:
                    System.out.print("A");
                case 1:
                    System.out.print("B");
                default:
                    System.out.print("C");
            }
        }
    }

    public static void main(String[] args) {
        PrintNum printA = new PrintNum(0);
        PrintNum printB = new PrintNum(1);
        PrintNum printC = new PrintNum(2);
        printA.start();
        printB.start();
        printC.start();
    }
0人推荐
随时随地看视频
慕课网APP