- 浏览: 1873307 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
July01:
最近了解到一款StratoIO打印控件,功能如下:1、Html ...
jquery打印指定的div -
GentlemanQc:
...
quartz系列(二)spring3.2.5与quartz2.1.7集群版集成简要说明 -
静夜独窗:
你好,能说一下server.xml增加的配置是怎么影响性能的吗 ...
tomcat7.0性能优化-挑战极限精简版 -
beyondfengyu:
beyondfengyu 写道如果每个客户进程的时间不同步,时 ...
java并发(二十二)分布式锁 -
beyondfengyu:
如果每个客户进程的时间不同步,时间超前的进程是不是更容易得到锁 ...
java并发(二十二)分布式锁
活锁:一个线程通常会有会响应其他线程的活动。如果其他线程也会响应另一个线程的活动,那么就有可能发生活锁。同死锁一样,发生活锁的线程无法继续执行。然而线程并没有阻塞——他们在忙于响应对方无法恢复工作。这就相当于两个在走廊相遇的人:Alphonse向他自己的左边靠想让Gaston过去,而Gaston向他的右边靠想让Alphonse过去。可见他们阻塞了对方。Alphonse向他的右边靠,而Gaston向他的左边靠,他们还是阻塞了对方。
死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。
例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。
该情况如下:
如果线程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.
因为锁发生在不同的请求中,并且对于一个事务来说不可能提前知道所有它需要的锁,因此很难检测和避免数据库事务中的死锁。
另外一个死锁的例子
死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。
例如,如果线程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.
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并发(三十四)协程kilim
2015-10-02 11:29 5560概述 对协程的技术已经觊觎很久,他有高性能的优点,但目前工具对 ... -
java并发(三十三)栅栏CyclicBarrier
2015-09-12 16:09 3347如果说CountDownLatch(闭锁)是一次性的, ... -
java并发(三十二)非阻塞算法
2014-06-25 14:08 1296如果在某算法中,一个线程的失败或挂起不会导致其他线程也 ... -
java并发(三十一)Amdahl定律
2014-05-19 16:15 2174阿姆达尔定律 阿姆达尔(Amdahl)定律是计算机系统设计的重 ... -
java并发(三十)闭锁CountDownLatch
2014-05-07 23:33 875CountDownLatch,一个同步辅助类,在完成一组正在其 ... -
java并发(二十九)构建高效且可伸缩的结果缓存
2014-04-23 11:52 1052概述 几乎所有应用程序,都会使用某种形式的缓存。重用之 ... -
java并发(二十八)并发随机数,原子变量,并发集合
2014-04-13 12:04 4052原子变量 java.util.concurrent.a ... -
java并发(二十七) 并发性标注
2014-04-07 10:49 8949一介绍 <dependency> < ... -
java并发(二十六)正确使用Volatile变量
2014-03-30 19:41 1359概述 您只能在有限的一 ... -
java并发(二十五)java7之fork-join框架
2014-03-26 14:12 11454如果让我个人理解什么 ... -
java并发(二十四)多线程结果组装
2014-03-24 22:01 3890本文主要介绍多线程的 ... -
java并发(二十三)阻塞、非阻塞、同步、异步
2014-03-14 17:01 1409因为中文语意的问题, ... -
java并发(二十二)分布式锁
2014-03-12 16:24 18623Redis有一系列的命令, ... -
java并发(二十一)剖析同步器
2014-03-11 18:03 1173虽然许多同步器(如锁,信号量,阻塞队列等)功能上各不相同,但它 ... -
java并发(二十)线程池
2014-03-10 15:02 3864基本介绍 线程池(Thread Pool)对于限制应用程序中同 ... -
java并发(十九)阻塞队列
2014-03-10 14:46 1323阻塞队列与普通队列的 ... -
java并发(十八)信号量
2014-03-10 14:03 1229Semaphore(信号量) 是一个线程同步结构,用于在线程间 ... -
java并发(十七)重入锁死
2014-03-10 11:33 1095重入锁死与死锁和嵌套管程锁死非常相似。当一个线程重新获取锁,读 ... -
java并发(十六)Java中的读/写锁
2014-03-10 10:17 1172相比Java中的锁(Locks in Ja ... -
java并发(十五)Java中的锁
2014-03-09 12:07 931锁像synchronized同步块一样,是一种线程同步机制,但 ...
相关推荐
java高并发程序设计视频全集,并发场景,死锁,活锁,阻塞,非阻塞...
此外,书中还深入剖析了并发编程中的常见问题,如死锁、活锁、饥饿等,并提供了相应的解决方案和最佳实践。 本书注重理论与实践相结合,通过大量的示例代码和案例分析,帮助读者更好地理解和掌握并发编程的技巧和...
2万字Java并发编程面试题合集(含答案,建议收藏) 具体如下 1、在 java 中守护线程和本地线程区别?2、线程与进程的区别?3、什么是多线程中的上下文切换?4、死锁与活锁的区别,死锁与饥饿的区别?5、Java 中用到...
o活跃性问题 :死锁、活锁、饥饿 o性能问题 : 使用无锁结构 :TLS,Copy-On-Write,乐观锁;Java的原子类,Disruptor无锁队列 减少锁的持有时间 :让锁 细粒 度。如ConcurrentHashmap;再如读写锁,读...
Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 ...
Java并发编程实战 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及...
餐饮哲学家经典的并发问题,用Java解决避免死锁、活锁和资源匮乏。
死锁,活锁 上下文切换 多核和“现代”并发构造 ###数学 离散数学 计数问题 90m 概率问题 90m ###安卓 一般90m 并发 60m Looper & Handler 120m ###Java 仿制药 60m 关闭 拉姆达 30m AOP 150m 并发 390m 动态...
《实战java高并发程序设计》源码整理联系作者十三的java的学习交流QQ群: 881582471 , 658365129(已满)相关文章书籍封面目录第1章走入并行世界1.1何去何从的并行计算1.1.1忘掉那该死的并行1.1.2可怕的现实:摩尔...
介绍了并发的基础理论:同步/异步,并发/并行,临界区,阻塞/非阻塞,死锁/饥饿/活锁,并发级别,定律,JMM中的原子性,可见性,有序性等。 第二章Java并行程序基础: 介绍了thread的基本操作,volatile与...
最常见的一种活性问题,死锁,以及另外两种活性问题,饥饿和活锁。 僵局 死锁描述了两个或多个线程被永远阻塞、互相等待的情况。 饥饿 饥饿描述了线程无法定期访问共享资源并且无法取得进展的情况。 当共享资源被...
源码《实战Java高并发程序设计》 上的代码 葛一鸣郭超着 书籍封面: 注意事项 再运行代码之前,先用IDE将lib/jmatrices0.6.jar导入项目依赖中,因为书籍的第五章用到了这个库,然而这个库在maven仓库中又没有找到,只好...