LeedCode多线程第六题-交替打印字符串
一.题目如下:
解题思路:
这里先说一下我自己的解题思路.
- 使用三个信号量控制其他打印字母的线程是否为可执行.
- 使用LockSupport.park(),unpark()控制主线程(打印普通数字的线程)是否可执行.
注意点:
当线程执行acquire()方法获取不到信号量时,就会进入WAITING状态,一直在这等待,主线程执行完毕后,这几个线程还在等待,导致程序永远不会执行完,所以在开始的时候把几个线程都存到队列中,在主线程执行完后一一进行中断操作,保证线程正常退出.
class FizzBuzz {
private int n;
public FizzBuzz(int n) {
this.n = n;
}
Semaphore fizzSemaphore=new Semaphore(0);
Semaphore buzzSemaphore=new Semaphore(0);
Semaphore fizzbuzzSemaphore=new Semaphore(0);
BlockingQueue<Thread> threadQueue = new LinkedBlockingQueue<>(1);
ArrayList<Thread> threadList=new ArrayList<>(3);
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
threadList.add(Thread.currentThread());
for(;;){
try{
fizzSemaphore.acquire();
}catch(InterruptedException e){
break;
}
printFizz.run();
LockSupport.unpark(threadQueue.poll());
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
threadList.add(Thread.currentThread());
for(;;){
try{
buzzSemaphore.acquire();
}catch(InterruptedException e){
break;
}
printBuzz.run();
LockSupport.unpark(threadQueue.poll());
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
threadList.add(Thread.currentThread());
for(;;){
try{
fizzbuzzSemaphore.acquire();
}catch(InterruptedException e){
break;
}
printFizzBuzz.run();
LockSupport.unpark(threadQueue.poll());
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i++){
if(i % 3!=0 && i % 5!=0){
printNumber.accept(i);
}else if(i % 5 !=0){
threadQueue.put(Thread.currentThread());
fizzSemaphore.release();
LockSupport.park();
}else if(i % 3!=0){
threadQueue.put(Thread.currentThread());
buzzSemaphore.release();
LockSupport.park();
}else{
threadQueue.put(Thread.currentThread());
fizzbuzzSemaphore.release();
LockSupport.park();
}
}
//将其他处于WAITING状态的线程中断退出
for(int i=0;i<threadList.size();i++){
Thread curThread=threadList.get(i);
curThread.interrupt();
}
}
}
二.CyclicBarrier.
1.介绍.
这是Java并发包中的一个工具类java.util.concurrent.CyclicBarrier.
简单来说该工具类可以控制一组线程进行同步操作,也就是在一组线程都触发时才执行后续的代码,如果某个线程先触发,就会进入等待,直到一组中的最后一个线程完成触发操作.
有时候语言可能不太直观,看一下CyclicBarrier在该题目中的应用就理解了.
class FizzBuzz {
private int n;
//当四个线程都触发后才执行之后的代码
private static CyclicBarrier barrier = new CyclicBarrier(4);
public FizzBuzz(int n) {
this.n = n;
}
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
for (int i = 1; i <= n; i++) {
if (i % 3 == 0 && i % 5 != 0) {
printFizz.run();
}
try {
barrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
for (int i = 1; i <= n; i++) {
if (i % 3 != 0 && i % 5 == 0) {
printBuzz.run();
}
try {
barrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
for (int i = 1; i <= n; i++) {
if (i % 3 == 0 && i % 5 == 0) {
printFizzBuzz.run();
}
try {
barrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
if (i % 3 != 0 && i % 5 != 0) {
printNumber.accept(i);
}
try {
barrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}