何必去在意自己与别人相比是否特别呢?即便只有一个人也好。只要有一个认为自己特别的人,我觉得那就足够了。 —— 《冰菓》
多线程和锁
多线程和锁作为并发编程的两个重要概念,在提升了程序性能的同时,也带来了一些编码的复杂性。锁的出现就是为了保证在多线程的时候操作一组资源数据的一致性,我们在给资源加上锁之后,只有拥有了这个锁的线程才能操作此资源,其余的线程只能排队。
举个例子:例如你去换衣间试衣服,你进去了把门锁上,那么在这期间谁都无法进入,等你开门出来,别人才能进入。
什么是死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。(百度百科)
通俗点说:死锁就是两个线程同时占用两个资源,但又在彼此等待对方释放锁
模拟死锁
public class Test {
public static final Object lock1 = new Object();
public static final Object lock2 = new Object();
public static void main(String[] args) {
/*模拟死锁*/
deadLock();
}
/***
* 模拟死锁
*/
public static void deadLock() {
/*线程1拥有lock1之后试图获取lock2*/
Thread thread1 = new Thread(() -> {
while (true) {
synchronized (Test.lock1) {
System.out.println("线程1:" + Thread.currentThread().getName() + " 执行成功");
try {
Thread.sleep(1000);
synchronized (Test.lock2) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
/*线程2拥有lock2之后试图获取lock1*/
Thread thread2 = new Thread(() -> {
while (true) {
synchronized (Test.lock2) {
System.out.println("线程2:" + Thread.currentThread().getName()+ " 执行成功");
try {
Thread.sleep(1000);
synchronized (Test.lock1) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread1.start();
thread2.start();
}
}
执行
线程1:Thread-0 执行成功
线程2:Thread-1 执行成功
到这里发现程序不在往下执行,卡在了这个瞬间。
因为我们使用线程1拥有锁lock1的同时试图获取lock2,在线程2拥有锁lock2的同时试图获取lock1,这样就会造成彼此都在等待对方释放锁资源,这样就形成了死锁
避免死锁
死锁发生的条件为
- 互斥,共享资源X和Y只能被一个线程占用
- 占有且等待,线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X
- 不可抢占,其他线程不能强行抢占线程T1占用的资源
- 循环等待,线程1等待线程2占用的资源,线程2等待线程1占有的资源,就是循环等待
预防死锁的策略
- 破坏占用且等待条件,可以一次性申请所有资源
- 破坏不可抢占条件
- 破坏循环等待条件,对资源进行排序,按序申请资源
例如上述代码例子,我们如何修改能避免死锁的产生呢?
我们只需要让thread1和thread2获取lock1和lock2的顺序相同即可.
代码如下:
public class Test {
public static final Object lock1 = new Object();
public static final Object lock2 = new Object();
public static void main(String[] args) {
/*模拟死锁*/
deadLock();
}
/***
* 模拟死锁
*/
public static void deadLock() {
/*线程1拥有lock1之后试图获取lock2*/
Thread thread1 = new Thread(() -> {
while (true) {
synchronized (Test.lock1) {
System.out.println("线程1:" + Thread.currentThread().getName() + " 执行成功");
try {
Thread.sleep(1000);
synchronized (Test.lock2) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
/*线程2拥有lock1之后试图获取lock2*/
Thread thread2 = new Thread(() -> {
while (true) {
synchronized (Test.lock1) {
System.out.println("线程2:" + Thread.currentThread().getName()+ " 执行成功");
try {
Thread.sleep(1000);
synchronized (Test.lock2) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread1.start();
thread2.start();
}
}