zoukankan      html  css  js  c++  java
  • 死锁机制

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

    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的那行的行锁

    这样相互等待,就死锁了

  • 相关阅读:
    前端开发者进阶之ECMAScript新特性--Object.create
    JS事件:target与currentTarget区别
    30分钟掌握ES6/ES2015核心内容
    百度跨域搜索demo
    <a>标签的SEO优化细节
    jQuery之异步Ajax请求使用
    小tips: zoom和transform:scale的区别
    JSP页面静态化总结之一使用URLRewrite实现url地址伪静态化
    web前端安全机制问题全解析
    【转】Asp.net MVC Comet推送
  • 原文地址:https://www.cnblogs.com/zemliu/p/2503725.html
Copyright © 2011-2022 走看看