1.按序打印
1114.按序打印
法1:CountDownLatch
用来控制一个或者多个线程等待多个线程。
维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await()
方法而在等待的线程就会被唤醒。
CountDownLatch stage1 = new CountDownLatch(1);
CountDownLatch stage2 = new CountDownLatch(1);
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
stage1.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
stage1.await();
printSecond.run();
stage2.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
stage2.await();
printThird.run();
}
法2:Semaphore
Semaphore 类似于操作系统中的信号量,可以控制对互斥资源的访问线程数。
Semaphore stage1 = new Semaphore(1);
Semaphore stage2 = new Semaphore(0);
Semaphore stage3 = new Semaphore(0);
public void first(Runnable printFirst) throws InterruptedException {
stage1.acquire();
printFirst.run();
stage2.release();
}
public void second(Runnable printSecond) throws InterruptedException {
stage2.acquire();
printSecond.run();
stage3.release();
}
public void third(Runnable printThird) throws InterruptedException {
stage3.acquire();
printThird.run();
}
法3:Lock
java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。
相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。使用 Lock 来获取一个 Condition 对象。
Lock lock = new ReentrantLock();
volatile int stage = 1;
Condition stage2 = lock.newCondition();
Condition stage3 = lock.newCondition();
public void first(Runnable printFirst) throws InterruptedException {
lock.lock();
try {
printFirst.run();
stage = 2;
stage2.signal();
}finally {
lock.unlock();
}
}
public void second(Runnable printSecond) throws InterruptedException {
lock.lock();
try {
while(stage!=2) {
stage2.await();
}
printSecond.run();
stage = 3;
stage3.signal();
}finally {
lock.unlock();
}
}
public void third(Runnable printThird) throws InterruptedException {
lock.lock();
try {
while(stage!=3) {
stage3.await();
}
printThird.run();
}finally {
lock.unlock();
}
}
法4:无锁
volatile int stage = 1;
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
stage = 2;
}
public void second(Runnable printSecond) throws InterruptedException {
while(stage!=2);
printSecond.run();
stage = 3;
}
public void third(Runnable printThird) throws InterruptedException {
while(stage!=3);
printThird.run();
}
法5:CyclicBarrier
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。
和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。
CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset()方法可以循环使用,所以它才叫做循环屏障。
CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction在所有线程都到达屏障的时候会执行一次。
CyclicBarrier cb1 = new CyclicBarrier(2);
CyclicBarrier cb2 = new CyclicBarrier(2);
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
try {
cb1.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public void second(Runnable printSecond) throws InterruptedException{
try {
cb1.await();
printSecond.run();
cb2.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public void third(Runnable printThird) throws InterruptedException{
try {
cb2.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
printThird.run();
}
2.交替打印FooBar
法1:
class FooBar {
private int n;
public FooBar(int n) {
this.n = n;
}
Semaphore stage1 = new Semaphore(1);
Semaphore stage2 = new Semaphore(0);
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
stage1.acquire();
printFoo.run();
stage2.release();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
stage2.acquire();
printBar.run();
stage1.release();
}
}
}
法2:公平锁
class FooBar {
private int n;
public FooBar(int n) {
this.n = n;
}
Lock lock = new ReentrantLock(true);
volatile boolean permitFoo = true;
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; ) {
lock.lock();
try {
if(permitFoo) {
printFoo.run();
i++;
permitFoo = false;
} else {
Thread.yield();
}
}finally {
lock.unlock();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; ) {
lock.lock();
try {
if(!permitFoo) {
printBar.run();
i++;
permitFoo = true;
} else {
Thread.yield();
}
}finally {
lock.unlock();
}
}
}
}
法3:无锁
class FooBar {
private int n;
public FooBar(int n) {
this.n = n;
}
volatile boolean permitFoo = true;
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; ) {
if(permitFoo) {
printFoo.run();
i++;
permitFoo = false;
}
Thread.sleep(1);
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; ) {
if(!permitFoo) {
printBar.run();
i++;
permitFoo = true;
}
Thread.sleep(1);
}
}
}
法4:CyclicBarrier
class FooBar {
private int n;
public FooBar(int n) {
this.n = n;
}
CyclicBarrier cb = new CyclicBarrier(2);
volatile boolean fin = true;
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(!fin);
printFoo.run();
fin = false;
try {
cb.await();
} catch (BrokenBarrierException e) {
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
try {
cb.await();
} catch (BrokenBarrierException e) {
}
printBar.run();
fin = true;
}
}
}
3.打印零与奇偶数
1116.打印零与奇偶数
法1:Semaphore
class ZeroEvenOdd {
private int n;
public ZeroEvenOdd(int n) {
this.n = n;
}
Semaphore z = new Semaphore(1);
Semaphore e = new Semaphore(0);
Semaphore o = new Semaphore(0);
public void zero(IntConsumer printNumber) throws InterruptedException {
for(int i=0; i<n;i++) {
z.acquire();
printNumber.accept(0);
if((i&1)==0) {
o.release();
}else {
e.release();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=2; i<=n; i+=2) {
e.acquire();
printNumber.accept(i);
z.release();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1; i<=n; i+=2) {
o.acquire();
printNumber.accept(i);
z.release();
}
}
}
法2:Lock
class ZeroEvenOdd {
private int n;
public ZeroEvenOdd(int n) {
this.n = n;
}
Lock lock = new ReentrantLock();
Condition z = lock.newCondition();
Condition num = lock.newCondition();
volatile boolean zTurn = true;
volatile int zIndex = 0;
public void zero(IntConsumer printNumber) throws InterruptedException {
for(;zIndex<n;) {
lock.lock();
try {
while(!zTurn) {
z.await();
}
printNumber.accept(0);
zTurn = false;
num.signalAll();
zIndex++;
}finally {
lock.unlock();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=2; i<=n; i+=2) {
lock.lock();
try {
while(zTurn || (zIndex&1)==1) {
num.await();
}
printNumber.accept(i);
zTurn = true;
z.signal();
}finally {
lock.unlock();
}
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1; i<=n; i+=2) {
lock.lock();
try {
while(zTurn || (zIndex&1)==0) {
num.await();
}
printNumber.accept(i);
zTurn = true;
z.signal();
}finally {
lock.unlock();
}
}
}
}
法3:无锁
class ZeroEvenOdd {
private int n;
public ZeroEvenOdd(int n) {
this.n = n;
}
volatile int stage = 0;
public void zero(IntConsumer printNumber) throws InterruptedException {
for(int i=0;i<n;i++) {
while(stage>0) {
Thread.yield();
}
printNumber.accept(0);
if((i&1)==0) {
stage = 1;
}else {
stage = 2;
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=2; i<=n; i+=2) {
while(stage!=2) {
Thread.yield();
}
printNumber.accept(i);
stage = 0;
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1; i<=n; i+=2) {
while(stage!=1) {
Thread.yield();
}
printNumber.accept(i);
stage = 0;
}
}
}
4.H2O生成
1117.H2O生成
法1:Semaphore
h每获取一次释放一个o许可,o每次获取两个许可(即2次h后执行一次o)
class H2O {
public H2O() {
}
Semaphore h = new Semaphore(2);
Semaphore o = new Semaphore(0);
public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
h.acquire();
// releaseHydrogen.run() outputs "H". Do not change or remove this line.
releaseHydrogen.run();
o.release();
}
public void oxygen(Runnable releaseOxygen) throws InterruptedException {
o.acquire(2);
// releaseOxygen.run() outputs "O". Do not change or remove this line.
releaseOxygen.run();
h.release(2);
}
}
法2:
class H2O {
public H2O() {
}
Lock lock = new ReentrantLock();
Condition cd = lock.newCondition();
volatile int index = 0;
public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
lock.lock();
try {
while (index == 2)
cd.await();
index++;
// releaseHydrogen.run() outputs "H". Do not change or remove this line.
releaseHydrogen.run();
cd.signalAll();
} finally {
lock.unlock();
}
}
public void oxygen(Runnable releaseOxygen) throws InterruptedException {
lock.lock();
try {
while (index < 2)
cd.await();
// releaseOxygen.run() outputs "O". Do not change or remove this line.
releaseOxygen.run();
index = 0;
cd.signalAll();
} finally {
lock.unlock();
}
}
}
5.交替打印字符串
法1:信号量
class FizzBuzz {
private int n;
public FizzBuzz(int n) {
this.n = n;
}
Semaphore f = new Semaphore(0);
Semaphore b = new Semaphore(0);
Semaphore fb = new Semaphore(0);
Semaphore num = new Semaphore(1);
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
for (int i = 3; i <= n; i += 3) {
if (i % 5 != 0) {
f.acquire();
printFizz.run();
num.release();
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
for (int i = 5; i <= n; i += 5) {
if (i % 3 != 0) {
b.acquire();
printBuzz.run();
num.release();
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
for (int i = 15; i <= n; i += 15) {
fb.acquire();
printFizzBuzz.run();
num.release();
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
num.acquire();
// printNumber.accept(i);
if (i % 3 == 0 && i % 5 == 0) {
fb.release();
} else if (i % 3 == 0 && i % 5 != 0) {
f.release();
} else if (i % 3 != 0 && i % 5 == 0) {
b.release();
} else {
printNumber.accept(i);
num.release();
}
}
}
}
法2:管程模型
class FizzBuzz {
private int n;
public FizzBuzz(int n) {
this.n = n;
}
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
volatile int state = -1;
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
for (int i = 3; i <= n; i += 3) {
if (i % 5 == 0)
continue;
lock.lock();
try {
while (state != 3)
cond.await();
printFizz.run();
state = -1;
cond.signalAll();
} finally {
lock.unlock();
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
for (int i = 5; i <= n; i += 5) {
if (i % 3 == 0)
continue;
lock.lock();
try {
while (state != 5)
cond.await();
printBuzz.run();
state = -1;
cond.signalAll();
} finally {
lock.unlock();
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
for (int i = 15; i <= n; i += 15) {
lock.lock();
try {
while (state != 15)
cond.await();
printFizzBuzz.run();
state = -1;
cond.signalAll();
} finally {
lock.unlock();
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
lock.lock();
try {
while (state != -1)
cond.await();
if (i % 15 == 0) {
state = 15;
} else if (i % 3 == 0) {
state = 3;
} else if (i % 5 == 0) {
state = 5;
} else {
printNumber.accept(i);
// state = -1;
}
cond.signalAll();
} finally {
lock.unlock();
}
}
}
}
当然也可以有多个condition对象,这个就不写了,复制一下别人的:
private ReentrantLock lock = new ReentrantLock();
private Condition fCond = lock.newCondition();
private Condition bCond = lock.newCondition();
private Condition fbCond = lock.newCondition();
private Condition nCond = lock.newCondition();
private volatile Boolean state = false;
/**
* use lock
* @param printFizz
* @throws InterruptedException
*/
// printFizz.run() outputs "fizz".
public void fizz1(Runnable printFizz) throws InterruptedException {
for (int i = 3; i <= n; i = i + 3) {
lock.lock();
try {
if (i % 5 != 0) {
while (!state) {
fCond.await();
}
printFizz.run();
state = false;
nCond.signal();
}
}finally {
lock.unlock();
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz1(Runnable printBuzz) throws InterruptedException {
for (int i = 5; i <= n; i = i + 5) {
lock.lock();
try {
if (i % 3 != 0) {
while (!state) {
bCond.await();
}
printBuzz.run();
state = false;
nCond.signal();
}
}finally {
lock.unlock();
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz1(Runnable printFizzBuzz) throws InterruptedException {
for (int i = 15; i <= n; i = i + 15) {
lock.lock();
try {
while (!state) {
fbCond.await();
}
printFizzBuzz.run();
state = false;
nCond.signal();
}finally {
lock.unlock();
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number1(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
lock.lock();
try {
while (state) {
nCond.await();
}
if (i % 3 == 0 && i % 5 == 0) {
fbCond.signal();
state = true;
}else if (i % 3 == 0) {
fCond.signal();
state = true;
}else if (i % 5 == 0) {
bCond.signal();
state = true;
}else {
printNumber.accept(i);
nCond.signal();
}
}finally {
lock.unlock();
}
}
}
作者:guessmyname
链接:https://leetcode-cn.com/problems/fizz-buzz-multithreaded/solution/javashi-xian-shi-yong-semaphore-huo-zhe-reentrantl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
法3:无锁
我喜欢这种方案,简洁明了。
class FizzBuzz {
private int n;
private volatile int state = -1;
public FizzBuzz(int n) {
this.n = n;
}
public void fizz(Runnable printFizz) throws InterruptedException {
for (int i = 3; i <= n; i += 3) {
if (i % 5 == 0)
continue;
while (state != 3) {
Thread.yield();
}
printFizz.run();
state = -1;
}
}
public void buzz(Runnable printBuzz) throws InterruptedException {
for (int i = 5; i <= n; i += 5) {
if (i % 3 == 0)
continue;
while (state != 5) {
Thread.yield();
}
printBuzz.run();
state = -1; //控制权交还给number()方法
}
}
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
for (int i = 15; i <= n; i += 15) { //只输出15的倍数
while (state != 15) {
Thread.yield();
}
printFizzBuzz.run();
state = -1; //控制权交还给number()方法
}
}
public void number(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; ++i) {
while (state != -1) {
Thread.yield();
}
if (i % 15 == 0)
state = 15;
else if (i % 5 == 0)
state = 5;
else if (i % 3 == 0)
state = 3;
else
printNumber.accept(i);
}
}
}
6.哲学家进餐
- 信号量模型与管程模型是线程间协作的两大利器,大部分情况下,二者可以互相转换。它们的区别在于,信号量模型可以让多个线程共享临界区,而管程模型是让线程独占临界区;
- CountDownLatch主要用于一个线程等待多个线程的场景,而CyclicBarrier则用于多个线程之间互相等待的场景。
参考资料:https://zhuanlan.zhihu.com/p/81626432