`
85977328
  • 浏览: 1908481 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java并发(十)死锁 & 活锁

 
阅读更多
活锁:一个线程通常会有会响应其他线程的活动。如果其他线程也会响应另一个线程的活动,那么就有可能发生活锁。同死锁一样,发生活锁的线程无法继续执行。然而线程并没有阻塞——他们在忙于响应对方无法恢复工作。这就相当于两个在走廊相遇的人:Alphonse向他自己的左边靠想让Gaston过去,而Gaston向他的右边靠想让Alphonse过去。可见他们阻塞了对方。Alphonse向他的右边靠,而Gaston向他的左边靠,他们还是阻塞了对方。

死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。

例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。
该情况如下:
//Thread 1  locks A, waits for B
//Thread 2  locks B, waits for A
这里有一个TreeNode类的例子,它调用了不同实例的synchronized方法:

public class TreeNode {
    TreeNode parent   = null; 
    List children = new ArrayList();
    public synchronized void addChild(TreeNode child){
        if(!this.children.contains(child)) {
            this.children.add(child);
            child.setParentOnly(this);
        }
    }
    public synchronized void addChildOnly(TreeNode child){
        if(!this.children.contains(child){
            this.children.add(child);
        }
    }
    public synchronized void setParent(TreeNode parent){
        this.parent = parent;
        parent.addChildOnly(this);
    }
    public synchronized void setParentOnly(TreeNode parent){
        this.parent = parent;
    }
}

如果线程1调用parent.addChild(child)方法的同时有另外一个线程2调用child.setParent(parent)方法,两个线程中的parent表示的是同一个对象,child亦然,此时就会发生死锁。下面的伪代码说明了这个过程:
TreeNode parent = new TreeNode();
TreeNode child = new TreeNode();
Thread 1: parent.addChild(child); //locks parent
          --> child.setParentOnly(parent);

Thread 2: child.setParent(parent); //locks child
          --> parent.addChildOnly(child)
首先线程1调用parent.addChild(child)。因为addChild()是同步的,所以线程1会对parent对象加锁以不让其它线程访问该对象。

然后线程2调用child.setParent(parent)。因为setParent()是同步的,所以线程2会对child对象加锁以不让其它线程访问该对象。

现在child和parent对象被两个不同的线程锁住了。接下来线程1尝试调用child.setParentOnly()方法,但是由于child对象现在被线程2锁住的,所以该调用会被阻塞。线程2也尝试调用parent.addChildOnly(),但是由于parent对象现在被线程1锁住,导致线程2也阻塞在该方法处。现在两个线程都被阻塞并等待着获取另外一个线程所持有的锁。

注意:像上文描述的,这两个线程需要同时调用parent.addChild(child)和child.setParent(parent)方法,并且是同一个parent对象和同一个child对象,才有可能发生死锁。上面的代码可能运行一段时间才会出现死锁。

这些线程需要同时获得锁。举个例子,如果线程1稍微领先线程2,然后成功地锁住了A和B两个对象,那么线程2就会在尝试对B加锁的时候被阻塞,这样死锁就不会发生。因为线程调度通常是不可预测的,因此没有一个办法可以准确预测什么时候死锁会发生,仅仅是可能会发生。

更复杂的死锁
死锁可能不止包含2个线程,这让检测死锁变得更加困难。下面是4个线程发生死锁的例子:
Thread 1  locks A, waits for B
Thread 2  locks B, waits for C
Thread 3  locks C, waits for D
Thread 4  locks D, waits for A
线程1等待线程2,线程2等待线程3,线程3等待线程4,线程4等待线程1。

数据库的死锁
更加复杂的死锁场景发生在数据库事务中。一个数据库事务可能由多条SQL更新请求组成。当在一个事务中更新一条记录,这条记录就会被锁住避免其他事务的更新请求,直到第一个事务结束。同一个事务中每一个更新请求都可能会锁住一些记录。

当多个事务同时需要对一些相同的记录做更新操作时,就很有可能发生死锁,例如:

Transaction 1, request 1, locks record 1 for update
Transaction 2, request 1, locks record 2 for update
Transaction 1, request 2, tries to lock record 2 for update.
Transaction 2, request 2, tries to lock record 1 for update.
因为锁发生在不同的请求中,并且对于一个事务来说不可能提前知道所有它需要的锁,因此很难检测和避免数据库事务中的死锁。

另外一个死锁的例子
package com.chinaso.search.executor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestLock {
    private static ExecutorService executor = Executors.newFixedThreadPool(10);
    private String name;

    /**
     * 同步方法1
     * @return
     */
    public synchronized String getName() {
        return name;
    }

    /**
     * 同步方法2
     * @param t
     */
    public synchronized void print(TestLock t) {
        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(name + " end sleep");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(t.getName() + " synchroinzed");
        }
    }

    public void setName(String name) {
        this.name = name;
    }

    public TestLock(String name) {
        this.name = name;
    }

    public static void main(String[] args) throws Exception {

        final TestLock t1 = new TestLock("t1");
        final TestLock t2 = new TestLock("t2");

        executor.execute(new Runnable() {
            @Override
            public void run() {
                t1.print(t2);
            }
        });

        executor.execute(new Runnable() {
            @Override
            public void run() {
                t2.print(t1);
            }
        });

    }
}

分享到:
评论
2 楼 85977328 2014-06-03  
request代表第几次请求
1 楼 ywu 2014-05-30  
额,数据库死锁的例子是不是有问题啊,我觉得应该是这样啊
Transaction 1, request 1, locks record 1 for update
Transaction 2, request 2, locks record 2 for update.
Transaction 1, request 1, tries to lock record 2 for update
Transaction 2, request 2, tries to lock record 1 for update.

相关推荐

    Java中常见死锁与活锁的实例详解

    Java 中的死锁和活锁是一种常见的并发编程问题,它们可能会导致程序崩溃或性能下降。在 Java 中,死锁和活锁的实例详解是指在多线程编程中,多个线程在访问共享资源时,可能会出现的阻塞、死锁和活锁问题。 顺序...

    java并发编程实战源码,java并发编程实战pdf,Java

    《Java并发编程实战》是Java并发编程领域的一本经典著作,它深入浅出地介绍了如何在Java平台上进行高效的多线程编程。这本书的源码提供了丰富的示例,可以帮助读者更好地理解书中的理论知识并将其应用到实际项目中。...

    java并发编程2

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。以下是对标题和描述中所提及的几个知识点的详细解释: 1. **线程与并发** - **线程*...

    java并发编程书籍

    Java并发编程是软件开发中的一个关键领域,尤其是在大型企业级应用和分布式系统中。通过学习相关的书籍,开发者可以深入理解如何有效地设计和实现高效的多线程应用程序,避免并发问题,如竞态条件、死锁、活锁等。...

    Java 并发编程实战.pdf

    《Java并发编程实战》这本书是关于Java语言中并发编程技术的经典著作。它详细介绍了如何在Java环境中有效地实现多线程程序和并发控制机制。在Java平台上,由于其本身提供了强大的并发编程支持,因此,掌握并发编程...

    (PDF带目录)《Java 并发编程实战》,java并发实战,并发

    《Java 并发编程实战》是一本专注于Java并发编程的权威指南,对于任何希望深入了解Java多线程和并发控制机制的开发者来说,都是不可或缺的参考资料。这本书深入浅出地介绍了如何在Java环境中有效地管理和控制并发...

    Java并发编程实战华章专业开发者书库 (Tim Peierls 等 美Brian Goetz).pdf

    《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南,由Tim Peierls等人与Brian Goetz合著,旨在帮助Java开发者理解和掌握在多线程环境中编写高效、安全的代码。这本书由拥有丰富经验的JDK并发大师及...

    java并发编程内部分享PPT

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。这份“java并发编程内部分享PPT”显然是一个深入探讨这一主题的资料,旨在帮助开发者...

    JAVA并发编程艺术pdf版

    《JAVA并发编程艺术》是Java开发者深入理解和掌握并发编程的一本重要著作,它涵盖了Java并发领域的核心概念和技术。这本书详细阐述了如何在多线程环境下有效地编写高效、可靠的代码,对于提升Java程序员的技能水平...

    Java并发编程从入门到精通(pdf)(附源码)

    本书首先会介绍Java并发编程的基础概念,包括线程的创建与管理、同步机制如synchronized关键字和Lock接口,以及如何避免常见的并发问题,如死锁、活锁和饥饿。这些基础知识是理解并发编程的基石,通过深入浅出的讲解...

    java并发编程与实践

    "Java并发编程与实践"文档深入剖析了这一主题,旨在帮助开发者理解和掌握如何在Java环境中有效地实现并发。 并发是指在单个执行单元(如CPU)中同时执行两个或更多任务的能力。在Java中,这主要通过线程来实现,...

    java并发编程艺术

    描述中反复提到“java并发编程艺术”,这暗示书中将详细介绍如何优雅地设计和管理线程,避免常见的并发问题,如死锁、竞态条件和活锁。 在Java中,同步是控制多个线程访问共享资源的方式,主要通过`synchronized`...

    Java并发编程书籍高清版

    书中可能包含了大量示例代码,通过实例解析Java并发编程中的常见问题,如死锁、活锁、饥饿现象,以及如何避免这些问题。此外,还可能涉及了Java内存模型(JMM)的理解,以及如何使用Lock接口和Condition来实现更细...

    Java并发编程实践高清pdf及源码

    《Java并发编程实践》是一本深入探讨Java多线程编程的经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和David Holmes等专家共同编写。这本书全面介绍了Java平台上的并发编程技术,是Java开发...

    ( Java并发程序设计教程.zip )高清版 PDF

    死锁、活锁和饥饿现象也是并发编程中常见的问题,书里会详细分析这些情况的成因,并提供避免和解决策略。此外,书中可能还会涉及线程优先级、守护线程、线程中断等特性,以及Java 5及以后版本引入的并发改进,如 ...

    Java并发编程常识-梁飞.rar

    此外,Java并发编程还需要理解死锁、活锁和饥饿等问题。死锁是两个或更多线程相互等待对方释放资源导致的僵局;活锁则是线程不断重试导致无法继续执行的情况;饥饿则是线程由于资源分配策略而永久无法获取资源执行的...

    java并发编程

    通过阅读这本书,你将学习如何避免常见的并发陷阱,如死锁、活锁和饥饿,以及如何进行有效的并发调试和性能优化。此外,书中还可能涉及Java内存模型、线程通信(如wait/notify机制)以及并发设计模式等高级主题。总...

    JAVA并发编程实践.pdf+高清版+目录 书籍源码

    《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。它涵盖了Java并发的核心概念、工具和最佳实践,旨在帮助读者在多线程环境下编写高效、安全的代码。 并发编程是现代软件开发中的关键技能,...

    Java并发编程从入门到精通源码.rar

    Java并发编程是Java开发中的重要领域,特别是在大型分布式系统、多线程应用和服务器端程序设计中不可或缺。这个资源包“Java并发编程从入门到精通源码.rar”显然是为了帮助开发者深入理解并掌握这一关键技能。它包含...

    java并发编程实践pdf笔记

    Java并发编程实践是Java开发中不可或缺的一个领域,它涉及到如何高效、正确地处理多线程环境中的任务。这本书的读书笔记涵盖了多个关键知识点,旨在帮助读者深入理解Java并发编程的核心概念。 1. **线程和进程的...

Global site tag (gtag.js) - Google Analytics