`

lock实现运行时死锁检测

阅读更多
java的多线程机制非常强大。其中死锁会导致程序无止境的等待,影响系统的正常运行,并且不是发生时立即被发现,一旦被发现定然是,系统出现了明显的功能性无法正常运行,甚至是系统崩溃。
下面我们讨论死锁的成因以及死锁的检测方法。

1、首先是系统运行时线程和锁之间的关系:
第一种情况:lock等待持有他们的thread将其释放。这些lock是由thread所持有,即thread获得了lock,其他的thread不能再获取到lock。

第二种情况:thread等待获取lock。如果此lock被其他的thead占用,那么此thread便只有等待其他thread将其释放。

2、死锁的产生
最简单的死锁产生的形式是:thread1 已经获取lock1,然后再去获取lock2,如果此时有thread2 已经获取了lock2 ,并且同时去获取lock1,那么就会发生死锁。

稍复杂的死锁的形式:
thread1:已经获得lock1
thread2:已经获得lock2,lock3。希望获得lock1
thread3:已经获得lock4。希望获得lock2

如果此时thread1 有需要获取到lock4,那么就可能会产生死锁。
3、死锁产生原因的分析:
从死锁的产生上,大家可以考虑到:如果将thread1 当成根节点,lock1是其子节点,thread2 是lock1的子节点,lock2,lock3 是thread2的子节点,同理:thread3 是lock2 的子节点,lock4 是thread3的子节点。如此可以看出thread与其相关的锁就是一个树结构,最上层是当前thread,其下是thread与lock交叉的分层。此树反应了一个thread所拥有的所有lock,然后是所有正在等待这些lock上thread,如此表示下去无论是直接还是间接,此树上的每个lock都在等待根thread最终将其释放,如此如果此thread需要对某个lock hard wait,它就不能在此树上,如此会产生一个循环,也就是死锁的成因。

4、下面的代码是实现了一个死锁检测的类,当产生死锁时抛出死锁异常。
注意:所有的锁的获取必须是lock形式,如果有synchrensed的锁形式,将不能被检测到。

下面这个例子展示了如何简单的检测到一个死锁。(当然效率是相当的低)
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

//
// This is a very very slow implementation of a ReentrantLock class and is not for
//   everyday usage. The purpose of this class is to test for deadlocks. The lock()
//   method now throws a DeadlockDetectedException, if a deadlock occurs.
//
public class DeadlockDetectingLock extends ReentrantLock {
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	// List of deadlock detecting locks.
    // This array is not thread safe, and must be externally synchronized
    //    by the class lock. Hence, it should only be called by static
    //    methods.
    private static List<DeadlockDetectingLock> deadlockLocksRegistry = new ArrayList<DeadlockDetectingLock>();

    private static synchronized void registerLock(DeadlockDetectingLock ddl) {
        if (!deadlockLocksRegistry.contains(ddl)){
        	deadlockLocksRegistry.add(ddl);
        }
             
    }

    private static synchronized void unregisterLock(DeadlockDetectingLock ddl) {
        if (deadlockLocksRegistry.contains(ddl)){
        	deadlockLocksRegistry.remove(ddl);
        }
             
    }

    // List of threads hard waiting for this lock.
    // This array is not thread safe, and must be externally synchronized
    //    by the class lock. Hence, it should only be called by static
    //    methods.
    private List<Thread> hardwaitingThreads = new ArrayList<Thread>();

    private static synchronized void markAsHardwait(List<Thread> l, Thread t) {
        if (!l.contains(t)) {
        	l.add(t);
        }
    }

    private static synchronized void freeIfHardwait(List<Thread> l, Thread t) {
        if (l.contains(t)) {
        	l.remove(t);
        }
    }

    //
    // Deadlock checking methods
    //
    // Given a thread, return all locks that are already owned
    // Must own class lock prior to calling this method
    private static Iterator<DeadlockDetectingLock> getAllLocksOwned(Thread t) {
        DeadlockDetectingLock current;
        ArrayList<DeadlockDetectingLock> results = new ArrayList<DeadlockDetectingLock>();

        Iterator<DeadlockDetectingLock> itr = deadlockLocksRegistry.iterator();
        while (itr.hasNext()) {
            current = (DeadlockDetectingLock) itr.next();
            if (current.getOwner() == t) {
            	results.add(current);
            }
        }
        return results.iterator(); 
    }

    // Given a lock, return all threads that are hard waiting for the lock
    // Must own class lock prior to calling this method
    private static Iterator<Thread> getAllThreadsHardwaiting(DeadlockDetectingLock l) {
        return l.hardwaitingThreads.iterator();
    }

    // Check to see if a thread can perform a hard wait on a lock
    private static synchronized boolean canThreadWaitOnLock(Thread t, DeadlockDetectingLock l) {
    	//先获取t已经获取的lock锁list
        Iterator<DeadlockDetectingLock> locksOwned = getAllLocksOwned(t);
        while (locksOwned.hasNext()) {
            DeadlockDetectingLock current = locksOwned.next();

            // Thread can't wait if lock is already owned. This is the end condition
            //      for the recursive algorithm -- as the initial condition should be
            //      already tested for.
            // t 获取 l 锁,如果此锁已经被需要获得l锁的线程获取,那么就是产生死锁的条件
            if (current == l) 
            	return false;
            //获取(等待获取当前线程已经获得的current锁)的线程组
            Iterator<Thread> waitingThreads = getAllThreadsHardwaiting(current);
            while (waitingThreads.hasNext()) {
                Thread otherthread = (Thread) waitingThreads.next();

                // In order for the thread to safely wait on the lock, it can't
                //   own any locks that have waiting threads that already owns
                //   lock. etc. etc. etc. recursively etc.
                if (!canThreadWaitOnLock(otherthread, l)) {
                    return false;
                }
            }
        }
        return true;
    }

    //
    // Core Constructors
    //
    public DeadlockDetectingLock() {
        this(false, false);
    }

    public DeadlockDetectingLock(boolean fair) {
        this(fair, false);
    }

    private boolean debugging;
    public DeadlockDetectingLock(boolean fair, boolean debug) {
        super(fair);
        debugging = debug;
        registerLock(this);
    }

    //
    // Core Methods
    //
    public void lock() {
        // Note: Owner can't change if current thread is owner. It is
        //       not guaranteed otherwise. Other owners can change due to
        //       condition variables.
    	//当前线程是否获得当前锁
        if (isHeldByCurrentThread()) {
            if (debugging){
            	 System.out.println("Already Own Lock");
            }
            super.lock();
            //把当前线程从  wait list(无限等待获取该锁的线程列表)中移除,因为该线程已经获得该锁
            freeIfHardwait(hardwaitingThreads, Thread.currentThread());
            return;
        }

        // Note: The wait list must be marked before it is tested because
        //       there is a race condition between lock() method calls.
        //如果当前线程没有获得当前锁,那么把当前线程加入到 wait list(无限等待获取该锁的线程列表)
        markAsHardwait(hardwaitingThreads, Thread.currentThread());
        
        //测试当前线程能否获取this当前锁
        if (canThreadWaitOnLock(Thread.currentThread(), this)) {
            if (debugging) {
            	System.out.println("Waiting For Lock");
            }
            super.lock();
            freeIfHardwait(hardwaitingThreads, Thread.currentThread());
            if (debugging) {
            	System.out.println("Got New Lock");
            }
        } else {
            throw new DeadlockDetectedException("DEADLOCK");
        }
    }

    //
    // Note: It is debatable whether this is a hard or soft wait. Even if
    //       interruption is common, we don't know if the interrupting thread
    //       is also involved in the deadlock. As a compromise, we'll just
    //       not allow interrupts. This method is disabled.
    public void lockInterruptibly() throws InterruptedException {
        lock();
    }

    //
    // Note: It is not necessary to override the tryLock() methods. These
    //     methods perform a soft wait -- there is a limit to the wait. It
    //     not possible to deadlock when locks are not waiting indefinitely.
    //

    // Note 1: Deadlocks are possible with any hard wait -- this includes
    //      the reacquitition of the lock upon return from an await() method.
    //      As such, condition variables will mark for the future hard
    //      wait, prior to releasing the lock.
    // Note 2: There is no need to check for deadlock on this end because
    //      a deadlock can be created whether the condition variable owns the
    //      lock or is reacquiring it. Since we are marking *before* giving
    //      up ownership, the deadlock will be detected on the lock() side
    //      first. It is not possible to create a new deadlock just by releasing
    //      locks.
    public class DeadlockDetectingCondition implements Condition {
	Condition embedded;
        protected DeadlockDetectingCondition(ReentrantLock lock, Condition embedded) {
	    this.embedded = embedded;
        }

        // Note: The algorithm can detect a deadlock condition if the thead is
        //    either waiting for or already owns the lock, or both. This is why
        //    we have to mark for waiting *before* giving up the lock.
        public void await() throws InterruptedException {
            try {
                markAsHardwait(hardwaitingThreads, Thread.currentThread());
                embedded.await();
            } finally {
                freeIfHardwait(hardwaitingThreads, Thread.currentThread());
            }
        }

        public void awaitUninterruptibly() {
            markAsHardwait(hardwaitingThreads, Thread.currentThread());
            embedded.awaitUninterruptibly();
            freeIfHardwait(hardwaitingThreads, Thread.currentThread());
        }

        public long awaitNanos(long nanosTimeout) throws InterruptedException {
            try {
                markAsHardwait(hardwaitingThreads, Thread.currentThread());
                return embedded.awaitNanos(nanosTimeout);
            } finally {
                freeIfHardwait(hardwaitingThreads, Thread.currentThread());
            }
        }

        public boolean await(long time, TimeUnit unit) throws InterruptedException {
            try {
                markAsHardwait(hardwaitingThreads, Thread.currentThread());
                return embedded.await(time, unit);
            } finally {
                freeIfHardwait(hardwaitingThreads, Thread.currentThread());
            }
        }

        public boolean awaitUntil(Date deadline) throws InterruptedException {
            try {
                markAsHardwait(hardwaitingThreads, Thread.currentThread());
                return embedded.awaitUntil(deadline);
            } finally {
                freeIfHardwait(hardwaitingThreads, Thread.currentThread());
            }
        }

	public void signal() {
	    embedded.signal();
	}

	public void signalAll() {
	    embedded.signalAll();
	}
    }

    // Return a condition variable that support detection of deadlocks
    public Condition newCondition() {
        return new DeadlockDetectingCondition(this, super.newCondition());
    }

    //
    // Testing routines here
    //
    // These are very simple tests -- more tests will have to be written a b c 都被注册到 deadlockLocksRegistry 中
    private static Lock a = new DeadlockDetectingLock(false, true);
    private static Lock b = new DeadlockDetectingLock(false, true);
    private static Lock c = new DeadlockDetectingLock(false, true);
    private static Condition wa = a.newCondition();
    private static Condition wb = b.newCondition();
    private static Condition wc = c.newCondition();

    private static void delaySeconds(int seconds) {
        try {
             Thread.sleep(seconds * 1000);
        } catch (InterruptedException ex) {
        }
    }

    private static void awaitSeconds(Condition c, int seconds) {
        try {
             c.await(seconds, TimeUnit.SECONDS);
        } catch (InterruptedException ex) {
        }
    }

    private static void testOne() {
         new Thread(new Runnable() {
             public void run() {
                 System.out.println("thread one grab a");
                 a.lock();
                 delaySeconds(2);
                 System.out.println("thread one grab b");
                 b.lock();
                 delaySeconds(2);
                 a.unlock(); b.unlock();
             }
         }).start();

         new Thread(new Runnable() {
             public void run() {
                 System.out.println("thread two grab b");
                 b.lock();
                 delaySeconds(2);
                 System.out.println("thread two grab a");
                 a.lock();
                 delaySeconds(2);
                 a.unlock(); b.unlock();
             }
         }).start();
    }

    private static void testTwo() {
         new Thread(new Runnable() {
             public void run() {
                 System.out.println("thread one grab a");
                 a.lock();
                 delaySeconds(2);
                 System.out.println("thread one grab b");
                 b.lock();
                 delaySeconds(10);
                 a.unlock(); b.unlock();
             }
         }).start();

         new Thread(new Runnable() {
             public void run() {
                 System.out.println("thread two grab b");
                 b.lock();
                 delaySeconds(2);
                 System.out.println("thread two grab c");
                 c.lock();
                 delaySeconds(10);
                 b.unlock(); c.unlock();
             }
         }).start();

         new Thread(new Runnable() {
             public void run() {
                 System.out.println("thread three grab c");
                 c.lock();
                 delaySeconds(4);
                 System.out.println("thread three grab a");
                 a.lock();
                 delaySeconds(10);
                 c.unlock(); a.unlock();
             }
         }).start();
    }
 
    private static void testThree() {
         new Thread(new Runnable() {
             public void run() {
                 System.out.println("thread one grab b");
                 b.lock();
                 System.out.println("thread one grab a");
                 a.lock();
                 delaySeconds(2);
                 System.out.println("thread one waits on b");
                 awaitSeconds(wb, 10);                 
                 a.unlock(); b.unlock();
             }
         }).start();

         new Thread(new Runnable() {
             public void run() {
                 delaySeconds(1);
                 System.out.println("thread two grab b");
                 b.lock();
                 System.out.println("thread two grab a");
                 a.lock();
                 delaySeconds(10);
                 b.unlock(); c.unlock();
             }
         }).start();

    }

    public static void main(String args[]) {
        int test = 1;
	if (args.length > 0)
	    test = Integer.parseInt(args[0]);
	switch(test) {
	    case 1:
                testOne();    // 2 threads deadlocking on grabbing 2 locks
		break;
	    case 2:
        	testTwo();    // 3 threads deadlocking on grabbing 2 out of 3 locks
		break;
	    case 3:
                testThree();  // 2 threads deadlocking on 2 locks with CV wait 
		break;
	    default:
	        System.err.println("usage: java DeadlockDetectingLock [ test# ]");
	}
        delaySeconds(60);
        System.out.println("--- End Program ---");
        System.exit(0);
    }
}

以上代码是:java Thread 第三版 第六章的代码,详细请查看原始代码
分享到:
评论

相关推荐

    sql server 死锁检测

    ### SQL Server 死锁检测详解 #### 死锁的基本概念 死锁是计算机科学领域一个重要的概念,尤其在数据库管理系统中极为关键。死锁的本质是一种僵持状态,它发生在两个或多个事务相互等待对方释放资源时,从而导致...

    linux 内核死锁检测

    ### Linux内核死锁检测——Lockdep详解 #### 一、Lockdep简介 **Lockdep** 是 Linux 内核提供的一项重要功能,用于协助开发者发现并解决死锁问题。死锁通常指的是两个或多个进程(在 Linux 内核中通常指的是任务)...

    sql数据库死锁查询工具

    - 使用系统视图:许多数据库系统提供了内置视图(如SQL Server的sys.dm_tran_locks,Oracle的v$lock),用于查看当前的锁定状态,帮助找出潜在的死锁。 - 错误日志:数据库系统通常会在发生死锁时记录错误信息,通过...

    orcale死锁查杀PB版

    Oracle数据库内置了死锁检测机制,当检测到死锁时,会自动选择一个受害者事务进行回滚,释放资源,从而打破死锁。另外,DBA可以通过以下方式手动检查: - 使用`v$session_wait`视图查找等待类型的`ENQ: TX - row ...

    多线死锁解决方案

    而"死锁监控方案.txt"可能包含了一种具体的死锁检测和处理的实现方法,如定时检测系统状态,一旦发现死锁迹象,就采取相应的恢复措施。 总结来说,解决多线程死锁问题需要从预防、避免、检测和恢复四个方面进行考虑...

    sqlite死锁datebaselock解决方案

    4. **死锁检测与恢复**:SQLite内建了死锁检测机制,当检测到死锁时,会回滚其中一个事务以解除死锁。但也可以在应用程序层面主动检测和处理死锁。 5. **使用WAL模式**:Write-Ahead Logging(预写日志)模式能有效...

    Sqlserver分析死锁进程

    SQL Server提供了多种工具和方法来检测和分析死锁,以便管理员能够及时发现并解决问题。本文将详细介绍如何在SQL Server中分析死锁进程,特别是如何通过存储过程来捕捉和分析死锁信息。 ### 死锁分析存储过程 在...

    通过SQL Server Profiler来监视分析死锁

    为了解决这个问题,SQL Server引入了死锁检测机制,并会在发现死锁时选择一个事务进行回滚,以打破僵局,这个过程称为死锁牺牲品。 SQL Server Profiler是SQL Server的一个重要工具,用于跟踪和记录服务器活动,...

    Java多线程程序死锁检查 JCarder

    除了JCarder,Java还提供了其他的死锁检测工具和手段,如JConsole、VisualVM和JDK自带的jstack命令。这些工具都可以帮助我们分析线程状态,找出可能导致死锁的原因。 在编写多线程程序时,预防死锁的一些最佳实践...

    查看阻塞和死锁

    通过使用如“sp_who_lock”这样的存储过程,数据库管理员可以获得系统状态的实时洞察,及时发现并解决阻塞和死锁问题,从而确保数据库系统的高效稳定运行。同时,合理设计事务逻辑,优化查询和索引,也是防止阻塞和...

    SQLServler自动杀死死锁进程

    - **超时机制**:SQL Server默认设置了一个超时时间,当事务等待超过这个时间仍未获得资源,就会触发死锁检测。系统会选择一个事务作为死锁的受害者,并中止该事务。 - **死锁监控**:通过创建警报或者触发器来...

    MYSQL 数据库死锁

    - 死锁检测:InnoDB有内置的死锁检测机制,会在事务等待一段时间(可通过`innodb_lock_wait_timeout`参数设置)后进行检查。 - 死锁解决:一旦检测到死锁,InnoDB会选择一个事务作为牺牲品,回滚该事务的操作,以...

    查看数据库死锁信息

    标题所提及的"查看数据库死锁信息"是一个至关重要的任务,因为死锁可能导致数据库性能下降,甚至影响到服务的正常运行。当出现"Deadlock found when trying to get to lock; try restarting transaction"这样的错误...

    分析SQL Server 数据库中的死锁

    ### 死锁检测与分析 #### 使用跟踪标志1204 在SQL Server中,启用跟踪标志1204是检测死锁的一个有效方式。当启用此标志后,SQL Server会在发生死锁时生成详细的死锁信息报告,包括涉及的事务、锁定的资源以及死锁...

    几种不常见的MySQL InnoDB 死锁情况--1

    2. 使用死锁检测机制,当检测到死锁时,MySQL会回滚其中一个事务以打破死锁循环。 3. 优化SQL查询,避免全表扫描,减少间隙锁的使用。 4. 考虑使用更宽松的事务隔离级别,如可重复读(Repeatable Read)降级为读已...

    oracle死锁故障分析和诊断解决

    3. **使用死锁检测和自动解决机制**:Oracle提供了死锁检测功能,一旦检测到死锁,系统会自动选择牺牲其中一个事务,解决死锁。 4. **优化SQL语句**:合理使用SQL提示(如`/*+ NO_MERGE */`),减少不必要的全表扫描...

    sqlserver死锁处理

    #### 二、死锁检测与处理 本节将详细介绍如何检测 SQL Server 中的死锁,并提供一种实用的解决方案来处理这些死锁问题。 ##### 1. 检测死锁 在 SQL Server 中,可以通过多种方式检测到死锁的发生: - **SQL ...

    mysql死锁解决

    #### 三、MySQL死锁检测 MySQL通过内部机制自动检测死锁。当检测到死锁时,通常会选择回滚其中一个事务来解除死锁。可以通过查询`INFORMATION_SCHEMA.INNODB_TRX`表来查看当前活跃的事务信息,包括事务的状态、开始...

Global site tag (gtag.js) - Google Analytics