背景
今天参加了一家公司的面试,问到了死锁的问题。想到死锁的四个充要条件也不记得了,有点慌。
正文
什么是死锁
用我自己的话说,就是不同的线程占据着不同的资源,想要访问对方,但是对自己占有的资源不放手。举个例子,家里有两个小孩,各自有一个玩具,有一天他们打起来了,问了下说是A想玩B的玩具,B想玩A的玩具,但是他们俩都表示,要对方先给,不给就不会把自己的玩具拿出来。虽然有很多不一样的地方,但是大体上的意思就是这样,如果这些条件不变的话,他们永远就再争吵。大概就是这样:
如果两个孩子愿意放手自己手里的玩具,或者没有刚好到了持有并去获取的状态,也不会出现这样的情况。这就要说明下死锁的四个条件了,这四个条件必须全部满足,死锁才能成立,破除其中的一个都会破坏死锁。
互斥条件、不可剥夺、请求与保持、循环等待
复习了下,但是就不展开来说了。
但是在代码里面,怎么能出现死锁呢?我想了想,脑袋一拍写了下面这个代码
public class DeathLock {
int c = 0;
int d = 0;
public DeathLock(){}
public static void main(String[] args) throws InterruptedException {
DeathLock deathLock = new DeathLock();
new Thread(() -> {
try {
deathLock.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
deathLock.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public void run() throws InterruptedException {
for(int i = 0; i <10;i++){
doingA();
doingB();
}
}
public synchronized void doingA() throws InterruptedException {
c++;
Thread.sleep(3000);
System.out.println(Thread.currentThread()+":"+ c);
}
public synchronized void doingB() throws InterruptedException {
d++;
Thread.sleep(3000);
System.out.println(Thread.currentThread()+":"+ d);
}
}
耻辱啊。居然能写出这样的代码。这明明就是顺序执行。
很明显缺少了循环等待的条件,那怎么做呢?
加进来,拍脑袋
private int flag = 0;
public Object o1=new Object();
public Object o2=new Object();
public void setFlag(int num){
this.flag=num;
}
@Override
public void run() {
if(flag == 0){
synchronized (o1){
System.out.println("o1"+Thread.currentThread().getName());
synchronized (o2){
System.out.println("o2"+Thread.currentThread().getName());
}
}
}
if(flag == 1){
synchronized (o2){
System.out.println("o2"+Thread.currentThread().getName());
synchronized (o1){
System.out.println("o1"+Thread.currentThread().getName());
}
}
}
}
public static void main(String[] args) {
DeathLock2 deathLocka = new DeathLock2();
DeathLock2 deathLockb = new DeathLock2();
deathLockb.flag=1;
Thread thread= new Thread(deathLocka,"threadA");
Thread thread2= new Thread(deathLockb,"threadB");
thread.start();
thread2.start();
}
但是貌似也没发生死锁,我明明对两个不同的对象都加了锁,分别走了不同的路径,相互争夺资源呀。
仔细想想,我创建了thread和thread2分别运行,在thread运行的时候,对deathLocka中的o1进行加锁,然后thread2运行的时候,对deathLockb中的o2进行加锁操作,再往后,在thread在o1还在加锁的状态下去尝试获取o2的锁,thread2也在加锁的状态下去获取o1的锁,看起来好像没问题。但是这里的deathLocka和deathLockb不是同一个对象啊兄弟。锁了deathLocka中的o1,但是没有锁到deathLockb中的o1,那不是白锁了吗。。。
这样我把o1,o2设置为静态对象,这样它就是线程共享的了。试试看果然发生了死锁。
public class DeathLock2 implements Runnable{
private int flag = 0;
public Object o1=new Object();
public Object o2=new Object();
public void setFlag(int num){
this.flag=num;
}
@Override
public void run() {
if(flag == 0){
synchronized (o1){
System.out.println("o1"+Thread.currentThread().getName());
synchronized (o2){
System.out.println("o2"+Thread.currentThread().getName());
}
}
}
if(flag == 1){
synchronized (o2){
System.out.println("o2"+Thread.currentThread().getName());
synchronized (o1){
System.out.println("o1"+Thread.currentThread().getName());
}
}
}
}
public static void main(String[] args) {
DeathLock2 deathLocka = new DeathLock2();
DeathLock2 deathLockb = new DeathLock2();
deathLockb.setFlag(1);
Thread thread= new Thread(deathLocka,"threadA");
Thread thread2= new Thread(deathLockb,"threadB");
thread.start();
thread2.start();
}
}
死锁是多线程中的一个重要问题,java中各种加锁的情况使用不当都可能造成死锁。
结束
周末了就早点休息,明天再写。