目录
- 死锁概念
- Java多线程中的死锁问题
- Java中如何查看是否产生死锁
- Java死锁经典问题——哲学家就餐问题
1,死锁概念
死锁是操作系统层面的一个概念,是进程死锁的简称,最早在 1965 年由 Dijkstra 在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一。在Java多线程中也常常出现死锁问题。
2,Java多线程中的死锁问题
多个线程共享多个资源,因资源使用不当而导致死锁问题。例如:A线程请求B线程正在使用的B资源,B线程请求C线程正在使用的C资源,C线程请求A线程正在使用的 A资源。于是就形成了死锁。
用图来清楚的表示一下
死锁产生的四个必要条件
1,互斥条件:当资源被占用时,其他线程不能使用。
2,请求与保持条件:一个线程因请求资源被阻塞时,它所占用的资源不会被释放。
3,不可剥夺条件:线程已获取资源,在未使用时不可被剥夺。
4,循环等待条件:各个线程等待资源形成了循环。
3,Java中如何查看是否产生死锁
首先看一下一个死锁的案例
public class Test3 {
public static void main(String[] args) {
/**
* 两把锁
*/
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 拿到lock1
*/
lock1.lock();
System.out.println("lock1锁住");
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 请求lock2
*/
lock2.lock();
lock1.unlock();
lock2.unlock();
System.out.println("lock1解锁");
}
});
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 拿到lock2
*/
lock2.lock();
System.out.println("lock2锁住");
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 请求lock1
*/
lock1.lock();
lock2.unlock();
lock1.unlock();
System.out.println("lock2解锁");
}
});
thread.setName("t1");
thread1.setName("t2");
thread.start();
thread1.start();
}
}
说明:t1线程拿到lock1 ,请求lock2。t2线程拿到lock2,请求lock1.所以产生了死锁。
我们怎样查看产生了死锁呢?
1,通过JDK自带的工具查看
jdk的bin目录下Jvisualvm工具
工具中就会提示产生死锁
点击线程Dump,查看死锁详细信息
2,通过jcmd命令查看线程id
再通过jstack + id查看死锁信息
4,
4,Java死锁经典问题——哲学家就餐问题
在一个圆桌上,有n个哲学家,n只筷子,每个哲学家左右两边各返一只筷子。哲学家可以进行思考和吃饭,思考时,不获取筷子。吃饭时,必须同时获得左右两只筷子才能吃饭(先获得右边,再获得左边)。
以下为Java实现的产生死锁的代码
/**
* 类说明
* 描述:TODO
* 哲学家类
*@author wj
*@date 2018年9月22日
*/
public class Philosopher extends Thread{
/**
* 左筷子
*/
Chopsticks left;
/**
* 右筷子
*/
Chopsticks right;
public Philosopher(Chopsticks left,Chopsticks right) {
// TODO Auto-generated constructor stub
this.left = left;
this.right = right;
}
/**
* 哲学家思考
*/
public void think(){
try {
Thread.currentThread().sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 哲学家吃饭
*/
public void eat(){
try {
Thread.currentThread().sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run(){
while(true) {
left.get();
System.out.println(Thread.currentThread().getName()+"哲学家拿起了左筷子");
right.get();
System.out.println(Thread.currentThread().getName()+"哲学家拿起了右筷子");
System.out.println(Thread.currentThread().getName()+"哲学家开始吃饭");
eat();
System.out.println(Thread.currentThread().getName()+"哲学家吃完了");
left.put();
System.out.println(Thread.currentThread().getName()+"哲学家放下了左筷子");
right.put();
System.out.println(Thread.currentThread().getName()+"哲学家放下了右筷子");
System.out.println(Thread.currentThread().getName()+"哲学家开始思考");
think();
System.out.println(Thread.currentThread().getName()+"哲学家思考的饿了,于是想吃饭");
}
}
}
/**
* 类说明
* 描述:TODO
* 筷子
*@author wj
*@date 2018年9月22日
*/
public class Chopsticks {
/**
* 筷子是否被拿到的标志
*/
boolean flag;
/**
* 拿起筷子
*/
public synchronized void get(){
while(flag) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
flag = true;
}
/**
* 放下筷子
*/
public synchronized void put(){
flag = false;
notifyAll();
}
}
/**
* 类说明
* 描述:TODO
*@author wj
*@date 2018年9月22日
*/
public class Test {
public static void main(String[] args) {
/**
* 筷子的个数
*/
int size = 5;
Chopsticks[] chopsticks = new Chopsticks[size];
for(int i = 0;i < chopsticks.length;i++) {
chopsticks[i] = new Chopsticks();
}
/**
* 五个哲学家
*/
for(int i = 0;i < chopsticks.length;i++) {
Philosopher philosopher = new Philosopher(chopsticks[i], chopsticks[(i+size+1)%size]);
philosopher.start();
}
}
}
运行结果
我们会发现这几个哲学家都拿起了左筷子,都在等待右筷子。所以就产生了死锁。
我们应该如何解决它呢?
我们只要打破它的四个必要条件就可以了。
解决方案:
在这里我们打破它的循环等待条件,让一个哲学家先拿右筷子后拿左筷子。
public class Test {
public static void main(String[] args) {
/**
* 筷子的个数
*/
int size = 5;
Chopsticks[] chopsticks = new Chopsticks[size];
for(int i = 0;i < chopsticks.length;i++) {
chopsticks[i] = new Chopsticks();
}
/**
* 五个哲学家
*/
for(int i = 0;i < chopsticks.length-1;i++) {
Philosopher philosopher = new Philosopher(chopsticks[i], chopsticks[(i+size+1)%size]);
philosopher.start();
}
/**
* 最后一个哲学家,将最后一个哲学家拿筷子顺序改变
*/
Philosopher philosopher = new Philosopher(chopsticks[0], chopsticks[4]);
philosopher.start();
}
}
这样就不会产生死锁了。