#LeetCode匠#按序打印

95 阅读2分钟

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」。

在多线程并发请求的业务场景中,如何让指定的顺序按需加载类或执行业务逻辑。下面通过对AQS包中的AtomicInteger、CountDownLatch,Semaphore的实际应用来解决按照一定的顺序执行问题。

题目描述

我们提供了一个类:

public class Foo {

public void first() { print("first"); }

public void second() { print("second"); }

public void third() { print("third"); }

}

三个不同的线程 A、B、C 将会共用一个 Foo 实例。

一个将会调用 first() 方法

一个将会调用 second() 方法

还有一个将会调用 third() 方法

请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。

题目示例

题目解法

解法一:AtomicInteger模拟锁的存在

原子性操作和锁的实现

/**
 * AtomicInter,累加器
 */
class Foo {

    private final AtomicInteger firstAtomic = new AtomicInteger(0);
    private final AtomicInteger secondAtomic = new AtomicInteger(0);

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
        firstAtomic.incrementAndGet();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        while(firstAtomic.get() != 1){
            // wait first
        }
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
        secondAtomic.incrementAndGet();
    }

    public void third(Runnable printThird) throws InterruptedException {
        while(secondAtomic.get() != 1){
            // wait second
        }
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }
}

解法二:使用CountDownLatch倒计时

倒时技术的工具类CountDownLatch

/**
 * CountDownLatch
 */
class Foo {

    private final CountDownLatch firstCountDown = new CountDownLatch(1);
    private final CountDownLatch secondCountDown = new CountDownLatch(1);

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
        firstCountDown.countDown();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        firstCountDown.await();
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
        secondCountDown.countDown();
    }

    public void third(Runnable printThird) throws InterruptedException {
        secondCountDown.await();
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }
}

解法三:Semaphore代表锁出战

使用信号量显示执行锁操作

/**
 * Semapure信号量
 */
class Foo {

    // 初始值为0,用于需要时release
    private final Semaphore secondSem = new Semaphore(0);
    private final Semaphore thirdSem = new Semaphore(0);

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
    
        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
        secondSem.release();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        // 等待2号信号获取非空信号量
        secondSem.acquire();
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
        thirdSem.release();
    }

    public void third(Runnable printThird) throws InterruptedException {
        // 等待3号信号获取非空信号量
        thirdSem.acquire();
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }
}

LeetCode原题链接:1114. 按序打印 - 力扣(LeetCode) (leetcode-cn.com)