一、synchronized死锁
我们先来看以下代码
示例
- A线程
package com.it.test.thread.consumer_product.dielock;
public class AThread extends Thread{
private Food food;
private Water water;
public AThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (water){
System.out.println("AThread拿到水了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (food){
System.out.println("AThread拿到食物了");
}
}
}
}
- B线程
public class BThread extends Thread{
private Food food;
public Water water;
public BThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (food){
System.out.println("BThread拿到食物了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (water){
System.out.println("BThread拿到水了");
}
}
}
}
- 测试
package com.it.test.thread.consumer_product.dielock;
public class DieLockTest {
public static void main(String[] args) {
Food food = new Food();
Water water = new Water();
AThread aThread = new AThread(food,water);
BThread bThread = new BThread(food,water);
aThread.start();
bThread.start();
}
}
AThread拿到水了
BThread拿到食物了
代码的设计是A线程拿到水,接着去拿食物,然后执行完毕。B线程先拿食物,然后拿水,然后执行完毕。
但是由于A线程和B线程拿线程的资源不对,导致A线程拿到了水之后,一直在等食物,却没有释放水。B线程拿到食物之后,一直在等待水,没有释放食物。导致两个线程处于一直等待拿锁的状态。这样就造成了死锁
解决办法
我们将A线程和B线程拿资源的顺序修改一下,使得他们都是先拿水,在拿食物试试。
package com.it.test.thread.consumer_product.dielock;
public class AThread extends Thread{
private Food food;
private Water water;
public AThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (water){
System.out.println("AThread拿到水了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (food){
System.out.println("AThread拿到食物了");
}
}
}
}
package com.it.test.thread.consumer_product.dielock;
public class BThread extends Thread{
private Food food;
public Water water;
public BThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (water){
System.out.println("BThread拿到水了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (food){
System.out.println("BThread拿到食物了");
}
}
}
}
AThread拿到水了
AThread拿到食物了
BThread拿到水了
BThread拿到食物了
二、产生死锁的条件
(1)多个线程抢占多个资源
(2)抢占资源的顺序不对
(3)拿到资源不放手
三、使用ReentrantLock尝试拿锁的机制解决死锁问题
- 创建两个线程争夺的资源实体,继承ReentrantLock。
package com.it.test.thread.consumer_product.dielock;
import java.util.concurrent.locks.ReentrantLock;
public class Food extends ReentrantLock {
}
package com.it.test.thread.consumer_product.dielock;
import java.util.concurrent.locks.ReentrantLock;
public class Water extends ReentrantLock {
}
- 创建两个线程竞争尝试获取资源
- AThread
package com.it.test.thread.consumer_product.dielock;
import java.util.Random;
public class AThread extends Thread{
private Food food;
private Water water;
public AThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
Random r = new Random();
while (true)
{
if(food.tryLock())
{
System.out.println("AThread拿到食物了");
if(water.tryLock()){
System.out.println("AThread拿到水了");
food.unlock();
water.unlock();
//跳出循环
break;
}
else{
System.out.println("AThread没有拿到水,放弃了食物");
food.unlock();
}
}
try {
Thread.sleep(r.nextInt(3));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- BThread
package com.it.test.thread.consumer_product.dielock;
public class BThread extends Thread{
private Food food;
public Water water;
public BThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
while (true){
if(water.tryLock())
{
System.out.println("BThread拿到水了");
if(food.tryLock())
{
System.out.println("BThread拿到食物了");
food.unlock();
water.unlock();
//跳出循环
break;
}
else{
System.out.println("BThread没有拿到食物,放弃了水");
water.unlock();
}
}
}
}
}
- 测试
package com.it.test.thread.consumer_product.dielock;
public class DieLockTest {
public static void main(String[] args) {
Food food = new Food();
Water water = new Water();
AThread aThread = new AThread(food,water);
BThread bThread = new BThread(food,water);
aThread.start();
bThread.start();
}
}
BThread拿到水了
AThread拿到食物了
BThread没有拿到食物,放弃了水
AThread没有拿到水,放弃了食物
BThread拿到水了
BThread没有拿到食物,放弃了水
AThread拿到食物了
AThread没有拿到水,放弃了食物
BThread拿到水了
BThread拿到食物了
AThread拿到食物了
AThread拿到水了
在最后A线程和B线程都拿到了食物和水。
在尝试拿锁的机制中,A线程和B线程中都开启了一个while循环,只有水和食物都拿到之后,才跳出循环。A线程尝试先拿食物,拿到食物之后尝试拿水,如果拿不到水,则将原来的食物也丢弃。B线程也一样,尝试先拿食物,拿到食物后再拿水,如果拿不到水,则将原来的食物也丢弃。在尝试拿锁的过程,我们给A线程做出一点点随机的休眠时间,错开。最后A和B线程都会拿到食物和水。
尝试拿锁的机制,就会产生一个问题就是:每次尝试拿锁的时候,A线程都是先拿到食物,B线程拿到水。他们在往下面再拿资源的时候,拿不到。然后就放弃原来的锁,重新拿。因此在程序上我们就做了一个时间的错开。这种一直尝试拿锁,又放弃锁的现象我们称为活锁。
网友评论