1.哲学家吃面问题:也就是说,有五个哲学家围成一圈,然后桌子上有五根筷子,每个人旁边都有一根筷子。这里就引出来了一个死锁问题,每个人都拥有其他人所需要的资源,但同时又等待其他人所拥有的资源,并且每个人在获得所有资源之前都不会放弃自己所拥有的资源。
2.常见的死锁问题:
1) 交叉锁导致的程序出现死锁,看一个例子
public class TestDemo2 {
//交叉锁导致的程序出现死锁
private final Object R1 = new Object();
private final Object R2 = new Object();
public void func1(){
synchronized (R1){
synchronized (R2){
//dosomething
}
}
}
public void func2(){
synchronized (R2){
synchronized (R1){
//dosomething
}
}
}
public static void main(String[] args) {
}
}
在这个例子中,我们可以看到A此时拥有了R1的锁,然后它在等待R2的锁,但是这时线程B拥有R2的锁,它在等待R1的锁,这时线程A和线程B都持有对方所需要的锁,但是谁都不会放开自己现在所持有的的锁,进入了无限的等待,这就形成了死锁问题。
2) 内存不足
假如有两个线程,threadA 获得了10MB memory , threadB 获得了20MB memory ,两个线程要执行的话都需要30MB ,但是此时系统可用内存只剩余20MB,这就会造成内存不足的状况。
3) 一问一答式的数据交换
比如server(服务器端)给client(客户端)开启了端口,等待client访问 ;client发送访问请求,等待接收;但是这时候server错过了client的请求,那么也就可能会造成死锁的问题。
4)死循环引起的死锁
(注意这个代码不要运行,否则可能会使得运行出现问题)
HashMap<String, String> map = new HashMap<>();
for(int i=0; i<2; i++){
new Thread(){
public void run(){
for(i = 1; i<Integer.MAX_VALUE; i++){
map.put(String.valueOf(i), String.valueOf(i));
}
}
}.start();
}
3.死锁问题发生的必要条件
1)互斥条件:每个资源在某一段时间只能由一个线程占用
2)不可抢占条件:线程所获得的资源在未使用完毕之前,其他线程不能强行去夺取
3)占有且申请条件:线程至少已经占有一个资源,又申请新的资源,该资源被另外的线程占有,该线程阻塞
4)循环等待条件:一个线程等待其他线程释放资源,其他线程又在等待另外一个线程释放资源,且最后有一个线程在等待第一个线程释放资源,这就使得大家都被锁住
那么我们如何避免死锁:就只能说让代码逻辑不同时满足以上4条。
4.哲学家吃面的代码:
class ChopSticks{
protected String name;
public ChopSticks(String name){
this.name = name;
}
}
class PhilosopherThread extends Thread{
private ChopSticks lefeChop;
private ChopSticks rightChop;
private String name;
public PhilosopherThread(String name, ChopSticks lefeChop, ChopSticks rightChop){
this.name = name;
this.lefeChop = lefeChop;
this.rightChop = rightChop;
}
@Override
public void run() {
//模拟吃面
synchronized (lefeChop){
System.out.println(name + " got the chopstick "+lefeChop.name);
synchronized (rightChop){
System.out.println(name + " got the chopstick "+rightChop.name);
System.out.println(name + "is eating noodles");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + " release the chopstick "+lefeChop.name+" and "+rightChop.name);
}
}
}
public class TestDemo{
public static void main(String[] args) {
ChopSticks chopStick0 = new ChopSticks("0");
ChopSticks chopStick1 = new ChopSticks("1");
ChopSticks chopStick2 = new ChopSticks("2");
ChopSticks chopStick3 = new ChopSticks("3");
ChopSticks chopStick4 = new ChopSticks("4");
new PhilosopherThread("thread0", chopStick0, chopStick1).start();
new PhilosopherThread("thread1", chopStick1, chopStick2).start();
new PhilosopherThread("thread2", chopStick2, chopStick3).start();
new PhilosopherThread("thread3", chopStick3, chopStick4).start();
new PhilosopherThread("thread4", chopStick4, chopStick0).start();
}
}
这时会造成死锁问题,那么我们就需要用wait(),notify(),notifyAll()机制来解决,使得哲学家吃之前必须保证左右两边筷子都可使用然后再去吃,代码如下:
class Chopsticks{
protected static HashMap<Integer, Boolean> map = new HashMap<>();
static{
map.put(0, false);
map.put(1, false);
map.put(2, false);
map.put(3, false);
map.put(4, false);
}
public synchronized void getChopStick(){
//首先获取跟当前线程名相同的chopstick -》curNo
String curName = Thread.currentThread().getName();
int curNo = Integer.parseInt(curName);
//另外一边即是nextNo = (curNo+1)%5
int nextNo = (curNo+1)%5;
//只要map.get(curNo)==true或者map.get(nextNo)==true
while(map.get(curNo) || map.get(nextNo)){
//当前线程阻塞 同时释放你所获得某一只筷子的使用权
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被唤醒后使用筷子进行吃面
map.put(curNo, true);
map.put(nextNo, true);
System.out.println("Thread "+curName+" got the chopstick "+curNo+" and "+nextNo );
}
public synchronized void freeChopStick(){
String curName = Thread.currentThread().getName();
int curNo = Integer.parseInt(curName);
int nextNo = (curNo+1)%5;
map.put(curNo, false);
map.put(nextNo, false);
this.notifyAll();
}
}
public class TestDemo{
public static void main(String[] args) {
Chopsticks chopSticks = new Chopsticks();
for(int i=0; i<5; i++){
new Thread(String.valueOf(i)){
@Override
public void run() {
while(true){
chopSticks.getChopStick();
try {
System.out.println("Thread "+Thread.currentThread().getName()+" is eating noodles");
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
chopSticks.freeChopStick();
}
}
}.start();
}
}
}
这时,发现哲学家吃面的死锁问题被解决了。