算法分类:并发编程
url:https://leetcode-cn.com/problems/print-foobar-alternately/
题目
1 | 1115. 交替打印FooBar |
Java解法
synchronized 解法
1 | class FooBar { |
ReentrantLock非公平锁
解法1:
1 | class FooBar { |
解法2:
1 | class FooBar { |
ReentrantLock 公平锁
1 | class FooBar { |
无锁实现
一、分析题意:
1、(问题一)首先是两条线程异步调用,两条线程不能确定他们谁先谁有,也就是有序性
2、(问题二)在线程执行的时候需要保证foo每次循环都在bar前,这样的话肯定是需要线程不能往下执行。
3、 解决以上两个问题基本就解决了,所以联想到以下两个关键字解决,volatile 可见性且有序,yield()让线程暂停。
二、首先解释两个用到的关键字 volatile yield();
yield :暂停当前正在执行的线程对象,yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执 行状态后马上又被执行。
volatile:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 (实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
三、解题过程
循环执行两次,首先确认目前的flag的状态以确保一定是foo执行(为了解决问题一,关键词volatile),来判断是否往下执 行,如果不是那么让当前不往下执行(关键词yield())
优化:可以在 yield() 时增限制,避免无限的循环。
1 | class FooBar { |
BlockingDeque 实现
BlockingDeque 是java.util.concurrent包中提供的一个接口。该接口表示一个双端队列。该双端队列,线程可以安全的插入,和取出元素。线程插入或者移出队列中的元素时,可能会阻塞。
一个线程可以插入元素到队列的任一端。如果队列full,那么线程将会阻塞,直到其他线程从队列中取出一个元素为止。如果队列empty,那么从队列中取元素的线程将会阻塞,直到其他线程插入一个元素为止。
1 | import java.util.concurrent.BlockingDeque; |
Semaphore 实现
acquire实现,核心代码如下:
1
2
3
4
5
6
7
8
9
10 >final int nonfairTryAcquireShared(int acquires) {
> for (;;) {
> int available = getState();
> int remaining = available - acquires;
> if (remaining < 0 ||
> compareAndSetState(available, remaining))
> return remaining;
> }
>}
>
acquires值默认为1,表示尝试获取1个许可,remaining代表剩余的许可数。
release实现,核心代码如下:
1
2
3
4
5
6
7
8
9
10
11 >protected final boolean tryReleaseShared(int releases) {
> for (;;) {
> int current = getState();
> int next = current + releases;
> if (next < current) // overflow
> throw new Error("Maximum permit count exceeded");
> if (compareAndSetState(current, next))
> return true;
> }
>}
>
releases值默认为1,表示尝试释放1个许可,next 代表如果许可释放成功,可用许可的数量。
1 | import java.util.concurrent.Semaphore; |
CyclicBarrier 实现
默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier已经到达屏障位置,线程被阻塞。
- 每当线程执行await,内部变量count减1,如果count!= 0,说明有线程还未到屏障处,则在锁条件变量trip上等待。
- 当count == 0时,说明所有线程都已经到屏障处,执行条件变量的signalAll方法唤醒等待的线程。
其中 nextGeneration方法可以实现屏障的循环使用:
- 重新生成Generation对象
- 恢复count值
1 | import java.util.concurrent.BrokenBarrierException; |
CountDownLatch + CyclicBarrier
1 | import java.util.concurrent.CountDownLatch; |
Python3解法
简单公平锁搞定
1 | from threading import Lock |