《Java并发编程的艺术》一一1.2 死锁

1.2 死锁

锁是个非常有用的工具,运用场景非常多,因为它使用起来非常简单,而且易于理解。但同时它也会带来一些困扰,那就是可能会引起死锁,一旦产生死锁,就会造成系统功能不可用。让我们先来看一段代码,这段代码会引起死锁,使线程t1和线程t2互相等待对方释
放锁。

public class DeadLockDemo {

    privat static String A = "A";
    private static String B = "B";

    public static void main(String[] args) {

            new DeadLockDemo().deadLock();
    }

    private void deadLock() {
            Thread t1 = new Thread(new Runnable() {
                    @Override
                    publicvoid run() {
                            synchronized (A) {
                                    try { Thread.currentThread().sleep(2000);
                                    } catch (InterruptedException e) {
                                            e.printStackTrace();
                                    }
                                    synchronized (B) {
                                            System.out.println("1");
                                    }
                            }
                    }
            });

            Thread t2 = new Thread(new Runnable() {
                    @Override
                    publicvoid run() {
                            synchronized (B) {
                                    synchronized (A) {
                                            System.out.println("2");
                                    }
                            }
                    }
            });

            t1.start();
            t2.start();
    }

}

这段代码只是演示死锁的场景,在现实中你可能不会写出这样的代码。但是,在一些更为复杂的场景中,你可能会遇到这样的问题,比如t1拿到锁之后,因为一些异常情况没有释放锁(死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉。
一旦出现死锁,业务是可感知的,因为不能继续提供服务了,那么只能通过dump线程查看到底是哪个线程出现了问题,以下线程信息告诉我们是DeadLockDemo类的第42行和第31行引起的死锁。

"Thread-2" prio=5 tid=7fc0458d1000 nid=0x116c1c000 waiting for monitor entry [116c1b000]
    java.lang.Thread.State: BLOCKED (on object monitor)
        at com.ifeve.book.forkjoin.DeadLockDemo$2.run(DeadLockDemo.java:42)
        - waiting to lock <7fb2f3ec0> (a java.lang.String)
        - locked <7fb2f3ef8> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:695)

"Thread-1" prio=5 tid=7fc0430f6800 nid=0x116b19000 waiting for monitor entry [116b18000]
    java.lang.Thread.State: BLOCKED (on object monitor)
        at com.ifeve.book.forkjoin.DeadLockDemo$1.run(DeadLockDemo.java:31)
        - waiting to lock <7fb2f3ef8> (a java.lang.String)
        - locked <7fb2f3ec0> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:695)

现在我们介绍避免死锁的几个常见方法。
避免一个线程同时获取多个锁。
避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

时间: 2024-11-08 19:25:28

《Java并发编程的艺术》一一1.2 死锁的相关文章

《Java 并发编程的艺术》迷你书

本文源自InfoQ发表的<Java 并发编程的艺术>电子书  作者:方腾飞  序言:张龙 免费下载此迷你书 推荐序 欣闻腾飞兄弟的<聊聊并发>系列文章将要集结成InfoQ迷你书进行发布,我感到非常的振奋.这一系列文章从最开始的发布到现在已经经历了两年多的时间,这两年间,Java世界发生了翻天覆地的变化.Java 7已经发布,而且Java 8也将在下个月姗姗来迟.围绕着JVM已经形成了一个庞大且繁荣的生态圈,Groovy.Scala.Clojure.Ceylon等众多JVM语言在蓬勃

《Java并发编程的艺术》-Java并发包中的读写锁及其实现分析

作者:魏鹏  本文是<Java并发编程的艺术>的样章 1. 前言 在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞.读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升. 除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式.假设在程序中定义一个共享

《Java并发编程的艺术》源码下载

<Java并发编程的艺术>纸质书购买地址=>天猫(价最低)  当当 京东  互动   亚马逊 <Java并发编程的艺术>电子书购买地址=>亚马逊 请使用JDK1.7及其以上版本编译源码,源码的任何问题可以通过评论告诉我们. 通过附件下载源码:ArtConcurrentBook 从SVN check out 源码:http://code.taobao.org/svn/ifevebook/trunk/ArtConcurrentBook   转载自 并发编程网 - ifeve

《Java并发编程的艺术》一一2.1 volatile的应用

2.1 volatile的应用 在多线程并发编程中synchronized和volatile都扮演着重要的角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的"可见性".可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值.如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度.本文将深入分析在硬件层面上Intel处理器是如何实现volatile的,

《Java并发编程的艺术》一一3.5 锁的内存语义

3.5 锁的内存语义 众所周知,锁可以让临界区互斥执行.这里将介绍锁的另一个同样重要,但常常被忽视的功能:锁的内存语义.3.5.1 锁的释放-获取建立的happens-before关系 锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 下面是锁释放-获取的示例代码. class MonitorExample { int a = 0; public synchronized void writer() { // 1 a++; //

《Java并发编程的艺术》一一1.1 上下文切换

1.1 上下文切换 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制.时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms). CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务.但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态.所以任务从保存到再加载的过程就是一次上下文切换. 这就像我们同时

《Java并发编程的艺术》一一2.4 本章小结

2.4 本章小结 本章我们一起研究了volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于本章介绍的volatile和原子操作的实现原理,了解这些原理对我们进行并发编程会更有帮助.

《Java并发编程的艺术》一一2.2 synchronized的实现原理与应用

2.2 synchronized的实现原理与应用 在多线程并发编程中synchronized一直是元老级角色,很多人都会称呼它为重量级锁.但是,随着Java SE 1.6对synchronized进行了各种优化之后,有些情况下它就并不那么重了.本文详细介绍Java SE 1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程.先来看下利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁.具体表现为以下3种形式.对于普通同步方法,

《Java并发编程的艺术》一一3.1 Java内存模型的基础

3.1 Java内存模型的基础 3.1.1 并发编程模型的两个关键问题在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递.在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信.同步是指程序中用于控制不同线程间操作发生相对

《Java并发编程的艺术》一一1.3 资源限制的挑战

1.3 资源限制的挑战 (1)什么是资源限制资源限制是指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源.例如,服务器的带宽只有2Mb/s,某个资源的下载速度是1Mb/s每秒,系统启动10个线程下载资源,下载速度不会变成10Mb/s,所以在进行并发编程时,要考虑这些资源的限制.硬件资源限制有带宽的上传/下载速度.硬盘读写速度和CPU的处理速度.软件资源限制有数据库的连接数和socket连接数等.(2)资源限制引发的问题在并发编程中,将代码执行速度加快的原则是将代码中串行执行的部分变