Leetcode_多线程
1、按序打印(1114、Easy)
1)题目要求
我们提供了一个类:
public class Foo {
public void first() { print(“first”); }
public void second() { print(“second”); }
public void third() { print(“third”); }
}
三个不同的线程将会共用一个 Foo 实例。
线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。
示例 1:
输入: [1,2,3]
输出: “firstsecondthird”
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 second() 方法,线程 C 将会调用 third() 方法。
正确的输出是 “firstsecondthird”。
示例 2:
输入: [1,3,2]
输出: “firstsecondthird”
解释:
输入 [1,3,2] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 third() 方法,线程 C 将会调用 second() 方法。
正确的输出是 “firstsecondthird”。
提示:
尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。
2)我的解法
class Foo {
private Lock lock=new ReentrantLock();
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private Condition c3=lock.newCondition();
private int cur=1;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
lock.lock();
if(cur!=1)c1.await();
printFirst.run();
cur=2;
c2.signal();
lock.unlock();
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
lock.lock();
if(cur!=2)c2.await();
printSecond.run();
cur=3;
c3.signal();
lock.unlock();
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
lock.lock();
if(cur!=3)c3.await();
printThird.run();
cur=1;
c1.signal();
lock.unlock();
}
}
3)其他解法
1、ReentrantLock
class Foo {
private int num;
private ReentrantLock lock;
private Condition c1;
private Condition c2;
private Condition c3;
public Foo() {
this.num = 1;
this.lock = new ReentrantLock();
this.c1 = lock.newCondition();
this.c2 = lock.newCondition();
this.c3 = lock.newCondition();
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
lock.lock();
try{
while(num!=1){
c1.await();
}
printFirst.run();
num = 2;
c2.signal();
}finally {
lock.unlock();
}
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
lock.lock();
try{
while(num!=2){
c2.await();
}
printSecond.run();
num = 3;
c3.signal();
}finally {
lock.unlock();
}
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
lock.lock();
try{
while(num!=3){
c3.await();
}
printThird.run();
}finally {
lock.unlock();
}
}
}
作者:ppppjqute
链接: link
来源:力扣(LeetCode)
2、synchronized
class Foo {
private boolean firstFinished;
private boolean secondFinished;
private Object lock = new Object();
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
synchronized (lock) {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
firstFinished = true;
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
synchronized (lock) {
while (!firstFinished) {
lock.wait();
}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
secondFinished = true;
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
synchronized (lock) {
while (!secondFinished) {
lock.wait();
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
}
}
作者:pulsaryu
链接:link
来源:力扣(LeetCode)
3、volatile
class Foo {
private volatile boolean firstJobDone = false;
private volatile boolean secondJobDone = false;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
firstJobDone=true;
}
public void second(Runnable printSecond) throws InterruptedException {
while (!firstJobDone){
//等待printFirst完成
}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
secondJobDone=true;
}
public void third(Runnable printThird) throws InterruptedException {
while (!secondJobDone){
//等待printSecond完成
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
}
作者:byte-b
链接: link
来源:力扣(LeetCode)
4)自己的优化代码
class Foo {
private volatile int cur=1;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
while(cur!=1){}
printFirst.run();
cur=2;
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
while(cur!=2){}
printSecond.run();
cur=3;
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
while(cur!=3){}
printThird.run();
cur=1;
}
}
5)学到的东西
JUC
synchronized
volatile
2、交替打印FooBar(1115、Medium)
1)题目要求
我们提供一个类:
class FooBar {
public void foo() {
for (int i = 0; i < n; i++) {
print(“foo”);
}
}
public void bar() {
for (int i = 0; i < n; i++) {
print(“bar”);
}
}
}
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。
请设计修改程序,以确保 “foobar” 被输出 n 次。
示例 1:
输入: n = 1
输出: “foobar”
解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,“foobar” 将被输出一次。
示例 2:
输入: n = 2
输出: “foobarfoobar”
解释: “foobar” 将被输出两次。
2)我的解法
1、
class FooBar {
private int n;
private volatile boolean IsFoo;
public FooBar(int n) {
this.n = n;
IsFoo=true;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(!IsFoo){Thread.yield();}
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
IsFoo=false;
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(IsFoo){Thread.yield();}
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
IsFoo=true;
}
}
}
2、
class FooBar {
private int n;
private Lock lock=new ReentrantLock();
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private boolean IsFoo;
public FooBar(int n) {
this.n = n;
IsFoo=true;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
if(!IsFoo)c1.await();
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
IsFoo=false;
c2.signal();
lock.unlock();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
if(IsFoo)c2.await();
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
IsFoo=true;
c1.signal();
lock.unlock();
}
}
}
3)其他解法
1、
class FooBar {
private ReentrantLock lock = new ReentrantLock();
private Condition fooCondition = lock.newCondition();
private Condition barCondition = lock.newCondition();
private int count = 1;
private int n;
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
if(count != 1) {
fooCondition.await();
}
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
barCondition.signal();
count=2;
lock.unlock();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
if(count != 2) {
barCondition.await();
}
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
fooCondition.signal();
count=1;
lock.unlock();
}
}
}
作者:cr6549
链接: link
来源:力扣(LeetCode)
2、
一、分析题意:
1、(问题一)首先是两条线程异步调用,两条线程不能确定他们谁先谁有,也就是有序性
2、(问题二)在线程执行的时候需要保证foo每次循环都在bar前,这样的话肯定是需要线程不能往下执行。
3、 解决以上两个问题基本就解决了,所以联想到以下两个关键字解决,volatile 可见性且有序,yield()让线程暂停。
二、首先解释两个用到的关键字 volatile yield();
yield :暂停当前正在执行的线程对象,yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执 行状态后马上又被执行。
volatile:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 (实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
三、解题过程
循环执行两次,首先确认目前的flag的状态以确保一定是foo执行(为了解决问题一,关键词volatile),来判断是否往下执 行,如果不是那么让当前不往下执行(关键词yield())
优化:可以在 yield() 时增限制,避免无限的循环。
class FooBar {
private int n;
private volatile int flag = 0;
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(flag != 0){
Thread.yield();
}
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();
flag = 0;
}
}
}
作者:hellowzqk
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
1、
class FooBar {
private int n;
private Lock lock=new ReentrantLock();
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private boolean IsFoo;
public FooBar(int n) {
this.n = n;
IsFoo=true;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
if(!IsFoo)c1.await();
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
IsFoo=false;
c2.signal();
lock.unlock();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
if(IsFoo)c2.await();
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
IsFoo=true;
c1.signal();
lock.unlock();
}
}
}
2、
class FooBar {
private int n;
private volatile boolean IsFoo;
public FooBar(int n) {
this.n = n;
IsFoo=true;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(!IsFoo){Thread.yield();}
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
IsFoo=false;
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
while(IsFoo){Thread.yield();}
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
IsFoo=true;
}
}
}
5)学到的东西
ReentrantLock、Condition
volatile ,需要thread.yield();
3、打印零与奇偶数(1116、Medium )
1)题目要求
假设有这么一个类:
class ZeroEvenOdd {
public ZeroEvenOdd(int n) { … } // 构造函数
public void zero(printNumber) { … } // 仅打印出 0
public void even(printNumber) { … } // 仅打印出 偶数
public void odd(printNumber) { … } // 仅打印出 奇数
}
相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:
线程 A 将调用 zero(),它只输出 0 。
线程 B 将调用 even(),它只输出偶数。
线程 C 将调用 odd(),它只输出奇数。
每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506… ,其中序列的长度必须为 2n。
示例 1:
输入:n = 2
输出:“0102”
说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 “0102”。
示例 2:
输入:n = 5
输出:“0102030405”
2)我的解法
class ZeroEvenOdd {
private int n;
private Lock lock=new ReentrantLock();
private Condition c0=lock.newCondition();
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private int cur=0;
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=1;i<=n;i++){
lock.lock();
while(cur!=0)c0.await();
printNumber.accept(0);
if(i%2!=0){cur=1;c1.signal();}
else{cur=2;c2.signal();}
lock.unlock();
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=2;i<=n;i+=2){
lock.lock();
while(cur!=2)c2.await();
printNumber.accept(i);
cur=0;
c0.signal();
lock.unlock();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i+=2){
lock.lock();
while(cur!=1)c1.await();
printNumber.accept(i);
cur=0;
c0.signal();
lock.unlock();
}
}
}
3)其他解法
1、
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();
}
}
}
}
作者:KevinBauer
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
class ZeroEvenOdd {
private int n;
private Lock lock=new ReentrantLock();
private Condition c0=lock.newCondition();
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private int cur=0;
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=1;i<=n;i++){
lock.lock();
while(cur!=0)c0.await();
printNumber.accept(0);
if(i%2!=0){cur=1;c1.signal();}
else{cur=2;c2.signal();}
lock.unlock();
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i=2;i<=n;i+=2){
lock.lock();
while(cur!=2)c2.await();
printNumber.accept(i);
cur=0;
c0.signal();
lock.unlock();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i+=2){
lock.lock();
while(cur!=1)c1.await();
printNumber.accept(i);
cur=0;
c0.signal();
lock.unlock();
}
}
}
5)学到的东西
同上
4、 哲学家进餐(1226、Medium)
1)题目要求
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
philosopher 哲学家的编号。
pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
eat 表示吃面。
putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
示例:
输入:n = 1
输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
解释:
n 表示每个哲学家需要进餐的次数。
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
output[i] = [a, b, c] (3个整数)
- a 哲学家编号。
- b 指定叉子:{1 : 左边, 2 : 右边}.
- c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。
提示:
1 <= n <= 60
2)我的解法
1、直接把方法修饰为synchronized,一次只让一个哲学家用餐
class DiningPhilosophers {
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public synchronized void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
pickLeftFork.run();
pickRightFork.run();
eat.run();
putLeftFork.run();
putRightFork.run();
}
}
2、
class DiningPhilosophers {
private ReentrantLock[] locks=new ReentrantLock[]{
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
private volatile int curNum=0;
public synchronized void add(){//必须加上synchronized,因为volatile不能保证原子性
curNum++;
}
public synchronized void sub(){//必须加上synchronized,因为volatile不能保证原子性
curNum--;
}
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 curLeft=-1,curRight=-1;
if(philosopher==0){curLeft=0;curRight=4;}
else if(philosopher==1){curLeft=1;curRight=0;}
else if(philosopher==2){curLeft=2;curRight=1;}
else if(philosopher==3){curLeft=3;curRight=2;}
else {curLeft=4;curRight=3;}
//如果有4个哲学家同时进餐,有死锁的风险,需自旋等待
while(curNum>=4)Thread.yield();
add();
locks[curLeft].lock();
pickLeftFork.run();
locks[curRight].lock();
pickRightFork.run();
eat.run();
putLeftFork.run();
locks[curLeft].unlock();
putRightFork.run();
locks[curRight].unlock();
sub();
}
}
3)其他解法
1、
class DiningPhilosophers {
//1个Fork视为1个ReentrantLock,5个叉子即5个ReentrantLock,将其都放入数组中
private final ReentrantLock[] lockList = {new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()};
//限制 最多只有4个哲学家去持有叉子
private Semaphore eatLimit = 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; //右边的叉子 的编号
eatLimit.acquire(); //限制的人数 -1
lockList[leftFork].lock(); //拿起左边的叉子
lockList[rightFork].lock(); //拿起右边的叉子
pickLeftFork.run(); //拿起左边的叉子 的具体执行
pickRightFork.run(); //拿起右边的叉子 的具体执行
eat.run(); //吃意大利面 的具体执行
putLeftFork.run(); //放下左边的叉子 的具体执行
putRightFork.run(); //放下右边的叉子 的具体执行
lockList[leftFork].unlock(); //放下左边的叉子
lockList[rightFork].unlock(); //放下右边的叉子
eatLimit.release();//限制的人数 +1
}
}
2、
class DiningPhilosophers {
//1个Fork视为1个ReentrantLock,5个叉子即5个ReentrantLock,将其都放入数组中
private final ReentrantLock[] lockList = {new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()};
//让 1个哲学家可以 “同时”拿起2个叉子(搞个临界区)
private ReentrantLock pickBothForks = 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; //右边的叉子 的编号
pickBothForks.lock(); //进入临界区
lockList[leftFork].lock(); //拿起左边的叉子
lockList[rightFork].lock(); //拿起右边的叉子
pickLeftFork.run(); //拿起左边的叉子 的具体执行
pickRightFork.run(); //拿起右边的叉子 的具体执行
pickBothForks.unlock(); //退出临界区
eat.run(); //吃意大利面 的具体执行
putLeftFork.run(); //放下左边的叉子 的具体执行
putRightFork.run(); //放下右边的叉子 的具体执行
lockList[leftFork].unlock(); //放下左边的叉子
lockList[rightFork].unlock(); //放下右边的叉子
}
}
作者:gfu
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
1、信号量
class DiningPhilosophers {
private ReentrantLock[] locks=new ReentrantLock[]{
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
private Semaphore limit=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 curLeft=-1,curRight=-1;
if(philosopher==0){curLeft=0;curRight=4;}
else if(philosopher==1){curLeft=1;curRight=0;}
else if(philosopher==2){curLeft=2;curRight=1;}
else if(philosopher==3){curLeft=3;curRight=2;}
else {curLeft=4;curRight=3;}
limit.acquire();
locks[curLeft].lock();
pickLeftFork.run();
locks[curRight].lock();
pickRightFork.run();
eat.run();
putLeftFork.run();
locks[curLeft].unlock();
putRightFork.run();
locks[curRight].unlock();
limit.release();
}
}
2、Atomic
class DiningPhilosophers {
private ReentrantLock[] locks=new ReentrantLock[]{
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
private AtomicInteger curNum=new AtomicInteger(0);
// 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 curLeft=-1,curRight=-1;
if(philosopher==0){curLeft=0;curRight=4;}
else if(philosopher==1){curLeft=1;curRight=0;}
else if(philosopher==2){curLeft=2;curRight=1;}
else if(philosopher==3){curLeft=3;curRight=2;}
else {curLeft=4;curRight=3;}
//如果有4个哲学家同时进餐,有死锁的风险,需自旋等待
while(curNum.get()>=4)Thread.yield();
curNum.getAndIncrement();
locks[curLeft].lock();
pickLeftFork.run();
locks[curRight].lock();
pickRightFork.run();
eat.run();
putLeftFork.run();
locks[curLeft].unlock();
putRightFork.run();
locks[curRight].unlock();
curNum.getAndDecrement();
}
}
5)学到的东西
信号量:Semaphore
类似于操作系统中的PV操作,acquire即P操作,release即V操作
Atomic类:Atomic
volatile不能保证原子性,++操作需用synchronized锁起来
信号量锁哲学家(如果锁叉子,需要在每次acquire时判断,因为每次acquire都有可能造成死锁)
ReentrantLock锁叉子