1114. 按序打印
多种解法,都是多线程常用工具
volite:
class Foo {
private volatile int flag=-1;
// private AtomicInteger firstJobDone = new AtomicInteger(0);
public Foo() {}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first".
printFirst.run();
// mark the first job as done, by increasing its count.
flag=1;
}
public void second(Runnable printSecond) throws InterruptedException {
while (flag != 1) {
// waiting for the first job to be done.
}
// printSecond.run() outputs "second".
printSecond.run();
// mark the second as done, by increasing its count.
flag=2;
}
public void third(Runnable printThird) throws InterruptedException {
while (flag != 2) {
// waiting for the second job to be done.
}
// printThird.run() outputs "third".
printThird.run();
}
}
原子类:
class Foo {
private AtomicInteger firstJobDone = new AtomicInteger(0);
private AtomicInteger secondJobDone = new AtomicInteger(0);
public Foo() {}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first".
printFirst.run();
// mark the first job as done, by increasing its count.
firstJobDone.incrementAndGet();
}
public void second(Runnable printSecond) throws InterruptedException {
while (firstJobDone.get() != 1) {
// waiting for the first job to be done.
}
// printSecond.run() outputs "second".
printSecond.run();
// mark the second as done, by increasing its count.
secondJobDone.incrementAndGet();
}
public void third(Runnable printThird) throws InterruptedException {
while (secondJobDone.get() != 1) {
// waiting for the second job to be done.
}
// printThird.run() outputs "third".
printThird.run();
}
}
阻塞队列:
class Foo {
BlockingQueue queue=new ArrayBlockingQueue(1);
BlockingQueue queue1=new ArrayBlockingQueue(1);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
System.out.println("111");
printFirst.run();
queue.add(1);
}
public void second(Runnable printSecond) throws InterruptedException {
queue.take();
System.out.println("222");
printSecond.run();
queue1.add(2);
queue.add(3);
}
public void third(Runnable printThird) throws InterruptedException {
queue1.take();
System.out.println("333");
queue.take();
printThird.run();
}
}
计时器:
class Foo {
private CountDownLatch second=new CountDownLatch(1);
private CountDownLatch three=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();
second.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
second.await();
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
three.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
three.await();
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
}
信号量:
class Foo {
public Semaphore seam_first_two = new Semaphore(0);
public Semaphore seam_two_second = new Semaphore(0);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
seam_first_two.release();
}
public void second(Runnable printSecond) throws InterruptedException {
seam_first_two.acquire();
printSecond.run();
seam_two_second.release();
}
public void third(Runnable printThird) throws InterruptedException {
seam_two_second.acquire();
printThird.run();
}
}
1115. 交替打印FooBar
class FooBar {
private int n;
private volatile int flag=-1;
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(flag!=-1){
//当前线程让步,否则超时
Thread.yield();
}
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
flag=1;
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(flag!=1){
//当前线程让步,否则超时
Thread.yield();
}
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
flag=-1;
}
}
}
1116. 打印零与奇偶数
class ZeroEvenOdd {
private int n;
private volatile int flag=0;
private volatile int count=1;
public ZeroEvenOdd(int n) {
this.n = n;
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void zero(IntConsumer printNumber) throws InterruptedException {
for(int i=0;i<n;i++){
while(flag!=0){
Thread.yield();
}
printNumber.accept(0);
if(count%2==0)
flag=2;
else
flag=1;
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=2;i<=n;i+=2){
while(flag!=2){
Thread.yield();
}
printNumber.accept(count++);
flag=0;
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i+=2){
while(flag!=1){
Thread.yield();
}
printNumber.accept(count++);
flag=0;
}
}
}
1117. H2O 生成
class H2O {
private Semaphore semaphoreH=new Semaphore(2);
private Semaphore semaphoreO=new Semaphore(1);
private CyclicBarrier cyclicBarrier=new CyclicBarrier(3);
public H2O() {
}
public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
semaphoreH.acquire();
try {
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
// releaseHydrogen.run() outputs "H". Do not change or remove this line.
releaseHydrogen.run();
semaphoreH.release();
}
public void oxygen(Runnable releaseOxygen) throws InterruptedException {
semaphoreO.acquire();
try {
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
// releaseOxygen.run() outputs "O". Do not change or remove this line.
releaseOxygen.run();
semaphoreO.release();
}
}
1195. 交替打印字符串
class FizzBuzz {
private volatile int count=1;
private volatile int flag=4;
private int n;
public FizzBuzz(int n) {
this.n = n;
}
public void setFlag(int count){
if(count%3==0&&count%5==0)
flag=3;
else if(count%3==0)
flag=1;
else if(count%5==0)
flag=2;
else
flag=4;
}
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
while (count<=n){
while (flag!=1){
if(count>n)
break;
Thread.yield();
}
if(count>n)
break;
printFizz.run();
setFlag(++count);
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
while (count<=n){
while (flag!=2){
if(count>n)
break;
Thread.yield();
}
if(count>n)
break;
printBuzz.run();
setFlag(++count);
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while (count<=n){
while (flag!=3){
if(count>n)
break;
Thread.yield();
}
if(count>n)
break;
printFizzBuzz.run();
setFlag(++count);
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
while (count<=n){
//其他线程并发修改导致n>16
//比如这个线程进入的时候n=15,
//另一个线程也是n=15
//这个线程让步,其他线程+1导致n=16
//n=16,其他线程不再进去
//while循环或者也是在自旋中,进入死锁
//flag没有人帮他修改,一直自旋。
while (flag!=4){
if(count>n)
break;
Thread.yield();
}
if(count>n)
break;
printNumber.accept(count);
setFlag(++count);
}
}
}
1226. 哲学家进餐
解法1:
class DiningPhilosophers {
//叉子是有顺序的,所以不能用信号量来表示,比如0号哲学家只能拿左右的叉子
//5把叉子对应五把锁,表示互斥变量 不可同时进入
private final ReentrantLock[] fork = {new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()};
//同时最多有4个人拿起叉子,就不会造成死锁
private Semaphore eatNumber = new Semaphore(4);
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
//左右叉子编号
int leftFork = (philosopher + 1) % 5;
int rightFork = philosopher;
//申请拿叉子
eatNumber.acquire();
//叉子上锁
fork[leftFork].lock();
fork[rightFork].lock();
//线程具体实现
pickLeftFork.run();
pickRightFork.run();
eat.run();
putLeftFork.run();
putRightFork.run();
//开锁
fork[leftFork].unlock();
fork[rightFork].unlock();
//释放
eatNumber.release();
}
}
方法二:
class DiningPhilosophers {
//保证每次只有一个人吃
//(改进:可以每次两个人吃,也就是第一种做法,需要对叉子加锁)
private Semaphore eatNumber = new Semaphore(1);
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
//申请拿叉子
eatNumber.acquire();
//线程具体实现
pickLeftFork.run();
pickRightFork.run();
eat.run();
putLeftFork.run();
putRightFork.run();
//释放
eatNumber.release();
}
}
解法3:
class DiningPhilosophers {
//1个Fork视为1个ReentrantLock,5个叉子即5个ReentrantLock,将其都放入数组中
private final ReentrantLock[] lockList = {new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()};
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = (philosopher + 1) % 5; //左边的叉子 的编号
int rightFork = philosopher; //右边的叉子 的编号
//编号为偶数的哲学家,优先拿起左边的叉子,再拿起右边的叉子
if (philosopher % 2 == 0) {
lockList[leftFork].lock(); //拿起左边的叉子
lockList[rightFork].lock(); //拿起右边的叉子
}
//编号为奇数的哲学家,优先拿起右边的叉子,再拿起左边的叉子
else {
lockList[rightFork].lock(); //拿起右边的叉子
lockList[leftFork].lock(); //拿起左边的叉子
}
pickLeftFork.run(); //拿起左边的叉子 的具体执行
pickRightFork.run(); //拿起右边的叉子 的具体执行
eat.run(); //吃意大利面 的具体执行
putLeftFork.run(); //放下左边的叉子 的具体执行
putRightFork.run(); //放下右边的叉子 的具体执行
lockList[leftFork].unlock(); //放下左边的叉子
lockList[rightFork].unlock(); //放下右边的叉子
}
}
方法4:
class DiningPhilosophers {
//0表示没人使用,1表示有人使用
//错误解法,volatile保证可见性,两个线程可能同时读到forkState等于0,但是提交通过了???,严谨一点应该通过AtomicInteger设置具体状态
private volatile int[] forkState=new int[5];
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = (philosopher + 1) % 5; //左边的叉子 的编号
int rightFork = philosopher; //右边的叉子 的编号
//编号为偶数的哲学家,优先拿起左边的叉子,再拿起右边的叉子
if (philosopher % 2 == 0) {
while (forkState[leftFork]==1){
Thread.sleep(1);
}
forkState[leftFork]=1;
while (forkState[rightFork]==1){
Thread.sleep(1);
}
forkState[rightFork]=1;
}
//编号为奇数的哲学家,优先拿起右边的叉子,再拿起左边的叉子
else {
while (forkState[leftFork]==1){
Thread.sleep(1);
}
forkState[leftFork]=1;
while (forkState[rightFork]==1){
Thread.sleep(1);
}
forkState[rightFork]=1;
}
pickLeftFork.run(); //拿起左边的叉子 的具体执行
pickRightFork.run(); //拿起右边的叉子 的具体执行
eat.run(); //吃意大利面 的具体执行
putLeftFork.run(); //放下左边的叉子 的具体执行
putRightFork.run(); //放下右边的叉子 的具体执行
forkState[rightFork]=0;
forkState[leftFork]=0;
}
}
解法的详细解释对应:https://leetcode-cn.com/problems/the-dining-philosophers/solution/1ge-semaphore-1ge-reentrantlockshu-zu-by-gfu/,方法1,2,3、4分别对应文中123