【手撕代码】多个线程交替打印

目  录:

方法一:锁实现【推荐】

方法二:信号量 semaphore 实现【推荐】

方法三:wait / notify 实现

方法四:使用 Lock / Condition 实现


面试的过程中,多个线程交替打印是经常被要求手写的。借用本文列出几种常用的解题方法。

方法一:锁实现【推荐】

times:保证每个线程的打印次数;

state:保证每个线程之间的交替打印;

lock:保证每次只有一个线程能够获取到资源。

public class PrintABCUsingLock {

    private int times;   // 控制打印次数
    private int state;   // 当前状态值:保证三个线程之间交替打印
    private Lock lock = new ReentrantLock();   // 保证每次只有一个线程能够拿到资源

    public PrintABCUsingLock(int times){
        this.times = times;
    }
    
    public void printA(){
        print("A", 0);
    }

    public void printB(){
        print("B", 1);
    }

    public void printC(){
        print("C", 2);
    }

    private void print(String name, int targetState){
        for(int i = 0; i < times;){
            lock.lock();
            // 控制交替打印
            if(state % 3 == targetState){
                state++;
                i++;
                System.out.println("线程:" + name + ",第 " + i + " 打印");
            }
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        PrintABCUsingLock printABC = new PrintABCUsingLock(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }
}

方法二:信号量 semaphore 实现【推荐】

  • 直接使用 semaphore 中的 state 是否为1,为1时代表可执行。
public class PrintABCUsingSemaphore {
    private int times;
    private Semaphore semaphoreA = new Semaphore(1);
    private Semaphore semaphoreB = new Semaphore(0);
    private Semaphore semaphoreC = new Semaphore(0);

    public PrintABCUsingSemaphore(int times){
        this.times = times;
    }

    public void printA() {
        try {
            print("A", semaphoreA, semaphoreB);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printB(){
        try {
            print("B", semaphoreB, semaphoreC);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC(){
        try {
            print("C", semaphoreC, semaphoreA);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void print(String name, Semaphore current, Semaphore next) throws InterruptedException {
        for(int i = 0; i < times; i++){
            current.acquire();
            System.out.println("线程:" + name + ",第 " + i + " 打印");
            next.release();
        }
    }

    public static void main(String[] args) {
        PrintABCUsingSemaphore printABC = new PrintABCUsingSemaphore(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }
}

方法三:wait / notify 实现

  • 需要使用 synchronized 保证线程安全。
public class PrintABCUsingWaitNotify {

    private int times;
    private int state;
    private Object objectA = new Object();
    private Object objectB = new Object();
    private Object objectC = new Object();

    public PrintABCUsingWaitNotify(int times){
        this.times = times;
    }

    public void printA(){
        try {
            print("A", 0, objectA, objectB);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printB(){
        try {
            print("B", 1, objectB, objectC);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC(){
        try {
            print("C", 2, objectC, objectA);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void print(String name, int targetState, Object current, Object next) throws InterruptedException {
        for(int i = 0; i < times;){
            // 锁定当前线程
            synchronized(current){
                while(state % 3 != targetState){
                    current.wait();
                }
                state++;
                i++;
                System.out.println("线程:" + name + ",第 " + i + " 打印");
                synchronized(next){
                    // 通知下一个线程
                    next.notify();
                }
            }
        }
    }

    public static void main(String[] args) {
        PrintABCUsingWaitNotify printABC = new PrintABCUsingWaitNotify(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }
}

方法四:使用 Lock / Condition 实现

public class PrintABCUsingLockCondition {

    private int times;
    private int state;
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    private Condition conditionC = lock.newCondition();

    public PrintABCUsingLockCondition(int times){
        this.times = times;
    }

    public void printA(){
        print("A", 0, conditionA, conditionB);
    }

    public void printB(){
        print("B", 1, conditionB, conditionC);
    }

    public void printC(){
        print("C", 2, conditionC, conditionA);
    }

    private void print(String name, int targetState, Condition current, Condition next){
        for(int i = 0; i < times;){
            lock.lock();
            try {
                while(state % 3 != targetState) {
                    // 当前线程等待
                    current.await();
                }
                state++;
                i++;
                System.out.println("线程:" + name + ",第 " + i + " 打印");
                // 通知下一个
                next.signal();
            } catch (InterruptedException e) {
                    e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        PrintABCUsingLockCondition printABC = new PrintABCUsingLockCondition(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }
}

 

你可能感兴趣的:(手撕代码)