死锁机制

  • 介绍: 死锁是由于不同的进程拥有对方需要的被锁资源,又在相互请求对方的被锁资源造成的无限等待局面

1 JAVA中的死锁

1.1 死锁示例

两个死锁类

 1 package deadlock;

 2 

 3 import java.util.List;

 4 

 5 public class A extends Thread {

 6     private List listA; //A,B两个类共享同一个listA和listB

 7     private List listB;

 8 

 9     public A(List listA, List listB) {

10         this.listA = listA;

11         this.listB = listB;

12     }

13 

14     public void lockListA() {

15         synchronized (listA) {  //请求listA的对象锁

16             System.out.println("listA locked");

17             try {

18                 sleep(2 * 1000);    //休息2秒

19             } catch (InterruptedException e) {

20                 e.printStackTrace();

21             }

22             synchronized (listB) {  //请求listB的对象锁

23                 listB.add("B");

24                 System.out.println(listB.toString());

25             }

26         }

27         System.out.println("listA unlocked");

28 

29     }

30 

31     public void run() {

32         lockListA();

33     }

34 }
 1 package deadlock;

 2 

 3 import java.util.List;

 4 

 5 public class B extends Thread{

 6     private List listA;

 7     private List listB;

 8 

 9     public B(List listA, List listB) {

10         this.listA = listA;

11         this.listB = listB;

12     }

13 

14     public void lockListB() {

15         synchronized (listB) {

16             System.out.println("listB locked");

17             try {

18                 sleep(5 * 1000);

19             } catch (InterruptedException e) {

20                 e.printStackTrace();

21             }

22             synchronized (listA) {

23                 listA.add("A");

24                 System.out.println(listA.toString());

25             }

26         }

27         System.out.println("listB unlocked");

28 

29     }

30 

31     public void run() {

32         lockListB();

33     }

34 }

 

运行主方法

 1 package deadlock;

 2 

 3 import java.util.ArrayList;

 4 import java.util.List;

 5 

 6 public class Test {

 7     public static void main(String[] args) {

 8         List a = new ArrayList();

 9         List b = new ArrayList();

10 

11         A threadA = new A(a, b);

12         B threadB = new B(a, b);

13 

14         threadA.start();

15         threadB.start();

16     }

17 }

 

运行结果: 

listA locked
listB locked

 可以看到locked以后就不能unlocked了,也就是进入了死锁,听我分析:

  1. threadA.start()调用了lockListA()方法,这时候listA的对象锁被threadA获取,然后threadA进入sleep 2秒的过程
  2. threadB.start()调用了lockListB()方法,这时候listB的对象锁被threadB获取,然后threadB进入sleep 5秒的过程
  3. 2秒后,threadA醒来,threadA请求listB的对象锁,此时threadB处于sleep状态,listB的对象锁被threadB持有,所以threadA坐等threadB释放对象锁
  4. 又过了3秒,threadB醒来,threadB请求listA的对象锁,此时threadA处于等待阻塞状态(正在等threadB释放listB的对象锁),还没有释放listA的锁,所以threadB也只有等待threadA释放listA的对象锁,于是threadB也进入阻塞状态
  5. 于是,threadA和threadB都进入了阻塞,于是就无限阻塞下去了...

 

1.2 同步方法的常用写法

一般来说,我们在写线程安全的方法时都会这样写,如下:

 1 package threadsafe;

 2 

 3 import java.util.ArrayList;

 4 import java.util.List;

 5 

 6 public class ThreadSafeClass {

 7     List<Integer> intList;

 8 

 9     public ThreadSafeClass() {

10         intList = new ArrayList<Integer>();

11     }

12 

13     public void addElement() {

14         synchronized (this) {   //调用此方法是,将自己锁死

15             Integer elem = (int)(Math.random() * 100);

16             intList.add(elem);

17         }

18     }

19 }
 1 package threadsafe;

 2 

 3 import java.util.ArrayList;

 4 import java.util.List;

 5 

 6 public class MyThread extends Thread{

 7     ThreadSafeClass threadSafeClass;

 8 

 9     public MyThread(ThreadSafeClass tsc) {

10         threadSafeClass = tsc;

11     }

12 

13     public void run() {

14         threadSafeClass.addElement();   //多线程执行此方法只能一步一步来,因为要获取本对象锁

15     }

16 }

 

 

1.4 JAVA自己实现的线程安全类

1.4.1 Vector一族

先谈谈Vector和ArrayList,我们都知道Vector和ArrayList功能类似,他们的区别在于Vector是线程安全的,而ArrayList不是,那么Vector的线程安全是如何实现的呢

查看源码可以发现他的方法多是synchronized的(这里我就不贴源码了,有兴趣的同学可以去查查),也就是说他的实现和我上面说的类似,这种方法的缺点其实就是效率比较低下,因为加锁是很消耗效率的

 

1.4.2 ConccurentLinkedQueue

这里以ConcurrentLinkedQueue为例,查看源码看不到synchronized之类的东西,这里引用网摘的一段话:

先来回顾之前提到过的ConcurrentHashMap,它是一个以Concurrent开头的并发集合类,其原理是通过增加锁和细化锁的粒度来提高并发度。
另一个值得一提的Concurrent是 ConcurrentLinkedQueue。这个类采用了另一种提高并发度的方式:非阻塞算法(Non-blocking),第一次实现了无锁的并发。

也就是说ConcurrentLinkedQueue并不是采用加锁的方式实现线程同步的,这个非阻塞算法大家有兴趣可以去研究一下

 

2 数据库中的死锁

数据库中的死锁常出现在事物中,这里我不写具体的代码了,举个例子随便聊聊

比如数据库中有两个表

死锁机制

现在又两个事务:

事务A:

1 BEGIN;

2 

3 SELECT * FROM user WHERE userId = ? FOR UPDATE;

4 

5 DELETE FROM message WHERE messageId = ?;

6 

7 COMMIT;

 

事务B:

1 BEGIN;

2 

3 SELECT * FROM message WHERE messageId = ? FOR UPDATE;

4 

5 UPDATE user SET userName = 'test' WHERE userId = ?;

6 

7 COMMIT;

 

假设现在两个事务同时运行,userId = 1, messageId = 1,就会造成死锁

因为事务A首先对userId=1的那行加上行锁,事务B也同时对messageId=1的那行加上行锁

然后事务A想DELETE messageId=1的那行,就必须等待事务B结束,释放messageId=1的那行的行锁

而事务B想UPDATE userId=1的那行,也必须等待事务A结束,释放userId=1的那行的行锁

这样相互等待,就死锁了

你可能感兴趣的:(死锁)