您的位置:首页 > 编程语言 > Java开发

java编程思想笔记-并发之死锁

2017-08-11 13:51 399 查看
以下四个条件同时满足时,就会发生死锁

1)互斥条件。任务使用的资源中至少有一个不能共享

2)至少有一个任务它必须持有一个资源且正在等待获取一个当前被别的任务持有的资源

3)资源不能被任务抢占,任务必须把资源释放当做普通事件

4)循环等待,一个任务等待其它任务所持有的资源,后者又在等待另一个任务所持有的资源,直到有一个任务在等待一个任务所持有的资源(打破死锁最容易的是破坏此条件)

死锁的典型示例:哲学家就餐问题,在一张圆形餐桌上,每个哲学家左边和右边分别放着两支筷子,哲学家先思考,思考结束后,先拿起右边筷子,再拿左边筷子,必须两支筷子都拿到才能够进餐,否则就进入等待状态,筷子的根数和哲学家人数是相同的

//筷子
public class Chopstick {
private boolean taken=false;
public synchronized void take() throws InterruptedException{
while (taken) {
wait();
}
taken=true;
}
public synchronized void drop(){
taken=false;
notifyAll();
}
}

//哲学家
public class Philosopher implements Runnable {
private Chopstick left;
private Chopstick right;
private final int id;
private final int ponderFactor;
private Random random=new Random(47);

public Philosopher(Chopstick left, Chopstick right, int id, int ponderFactor) {
this.left = left;
this.right = right;
this.id = id;
this.ponderFactor = ponderFactor;
}
private void pause() throws InterruptedException {
if (ponderFactor==0) {
return ;
}
TimeUnit.MILLISECONDS.sleep(random.nextInt(ponderFactor*250));
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
System.out.println(this+","+"thinking");
pause();
System.out.println(this+","+"grabbing right");
right.take();
System.out.println(this+","+"grabbing left");
left.take();
pause();
right.drop();
left.drop();
}
} catch (InterruptedException e) {
System.out.println(this+" exiting via interrupt "+id);
}
}

@Override
public String toString() {
return "Philosopher "+id;
}

}

public class DeadLockingDinningPhilosophers {
public static void main(String[] args) throws InterruptedException, IOException {
int ponder=5;
if (args.length>0) {
ponder=Integer.parseInt(args[0]);
}
int size=5;
if (args.length>1) {
size=Integer.parseInt(args[1]);
}
ExecutorService exec=Executors.newCachedThreadPool();
Chopstick[]chopsticks=new Chopstick[size];
for (int i = 0; i < size; i++) {
chopsticks[i]=new Chopstick();
}
for (int i = 0; i < chopsticks.length; i++) {
exec.execute(new Philosopher(chopsticks[i], chopsticks[(i+1)%size], i, ponder));
//exec.execute(new Philosopher(chopsticks[i], chopsticks[(i+1)%size], i, 0));
}
if (args.length==3&&args[2].equals("timeout")) {
TimeUnit.SECONDS.sleep(5);
}else {
System.out.println("Press Enter to quit");
System.in.read();
}
exec.shutdownNow();
}
}


ponderFactor 是思考因子,ponderFactor 越大那么思考时间越长,就越不会竞争筷子,发生死锁的可能性越小,但是仍然存在,如果ponderFactor 越小,那么筷子的竞争程度就越大,越容易发生死锁。哲学家的例子满足了产生死锁的四个条件

1.使用的筷子满足互斥条件,筷子不能共享

2.哲学家会拿起右边的筷子而等待左边的筷子

3.哲学家在持有右边筷子时候不能从左边的持有筷子的哲学家手里争抢筷子

4.右边的哲学家在等待左边哲学家手里的筷子,满足循环等待

打破死锁方案一:

我们通过打破第四个条件解决哲学死锁问题

修改哲学家拿筷子的方式,第一个哲学家先拿左边的筷子,再拿右边的筷子,并将哲学家的思考因子设置为0,增加筷子的竞争程度,观察是否发生了死锁

public class Philosopher2 implements Runnable {
private Chopstick left;
private Chopstick right;
private final int id;
private final int ponderFactor;
private Random random = new Random(47);

public Philosopher2(Chopstick left, Chopstick right, int id,
int ponderFactor) {
this.left = left;
this.right = right;
this.id = id;
this.ponderFactor = ponderFactor;
}

private void pause() throws InterruptedException {
if (ponderFactor == 0) {
return;
}
TimeUnit.MILLISECONDS.sleep(random.nextInt(ponderFactor * 250));
}

@Override
public void run() {
try {
while (!Thread.interrupted()) {
if (id > 0) {
System.out.println(this + "," + "thinking");
pause();
System.out.println(this + "," + "grabbing right");
right.take();
System.out.println(this + "," + "grabbing left");
left.take();
pause();
right.drop();
left.drop();
} else {
System.out.println(this + "," + "thinking");
pause();
System.out.println(this + "," + "grabbing left");
left.take();
System.out.println(this + "," + "grabbing right");
right.take();
pause();
right.drop();
left.drop();
}
}
} catch (InterruptedException e) {
System.out.println(this + " exiting via interrupt " + id);
}
}

@Override
public String toString() {
return "Philosopher " + id;
}
}


打破死锁方案二:

通过设定每个哲学家等待筷子的最大时间,如果超过了这一时间则放弃等待,并放弃已经持有的筷子

public class Chopstick2 {
private boolean taken = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

public boolean take() throws InterruptedException {
lock.lock();
try {
while (taken) {
boolean waitTimeOut=condition.await(100, TimeUnit.MILLISECONDS);
//判断是否是超时等待
if (!waitTimeOut) {
//false使放弃等待标志
return false;
}
}
taken = true;
} finally {
lock.unlock();
}
return true;
}

public void drop() {
lock.lock();
try {
taken = false;
condition.signalAll();
} finally {
lock.unlock();
}
}
}

public class Philosopher3 implements Runnable {
private Chopstick2 left;
private Chopstick2 right;
private final int id;
private final int ponderFactor;
private Random random=new Random(47);

public Philosopher3(Chopstick2 left, Chopstick2 right, int id, int ponderFactor) {
this.left = left;
this.right = right;
this.id = id;
this.ponderFactor = ponderFactor;
}
private void pause() throws InterruptedException {
if (ponderFactor==0) {
return ;
}
TimeUnit.MILLISECONDS.sleep(random.nextInt(ponderFactor*250));
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
System.out.println(this+","+"thinking");
pause();
System.out.println(this+","+"grabbing right");
//右边筷子是否拿到,还是超时等待而退出
boolean take=right.take();
//超时等重新进行循环尝试拿又筷子
if (!take) {
continue;
}
System.out.println(this+","+"grabbing left");
//左边筷子是否拿到,还是超时等待而退出
take=left.take();
//如果超时等待而退出则放弃已经拿到的又筷子
if (!take) {
System.out.println(this+"drop left ");
right.drop();
continue;
}
pause();
right.drop();
left.drop();
}
} catch (InterruptedException e) {
System.out.println(this+" exiting via interrupt "+id);
}
}

@Override
public String toString() {
return "Philosopher "+id;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: