letter Y A N. G Brass Letter F a n-spo D Pewter Uppercase Letter I N G
随笔 - 4, 文章 - 10, 评论 - 2, 引用 - 0

导航

<2025年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

公告

<A id=fs_1 title="letter Y" href="http://www.flickr.com/photos/49968232@N00/3212472314"><IMG alt="letter Y" src="http://static.flickr.com/3383/3212472314_5b6e6fe86b_s.jpg" border=0></A> <A id=fs_2 title=A href="http://www.flickr.com/photos/92745470@N00/2752700145"><IMG alt=A src="http://static.flickr.com/3100/2752700145_fdba82fb73_s.jpg" border=0></A> <A id=fs_3 title=N. href="http://www.flickr.com/photos/8701253@N08/2557725687"><IMG alt=N. src="http://static.flickr.com/3063/2557725687_7be05e2229_s.jpg" border=0></A> <A id=fs_4 title=G href="http://www.flickr.com/photos/95229107@N00/2429871767"><IMG alt=G src="http://static.flickr.com/2191/2429871767_fc380192ec_s.jpg" border=0></A> <A id=fs_5 title="Brass Letter F" href="http://www.flickr.com/photos/49968232@N00/2258452895"><IMG alt="Brass Letter F" src="http://static.flickr.com/2261/2258452895_ea8960e8ae_s.jpg" border=0></A> <A id=fs_6 title=a href="http://www.flickr.com/photos/92745470@N00/2753531212"><IMG alt=a src="http://static.flickr.com/3279/2753531212_b0caf6607b_s.jpg" border=0></A> <A id=fs_7 title=n-spo href="http://www.flickr.com/photos/63943575@N00/2575056456"><IMG alt=n-spo src="http://static.flickr.com/3189/2575056456_20e52769c3_s.jpg" border=0></A> <A id=fs_8 title=D href="http://www.flickr.com/photos/16324044@N00/2974100744"><IMG alt=D src="http://static.flickr.com/3242/2974100744_39a32e6e6f_s.jpg" border=0></A> <A id=fs_9 title="Pewter Uppercase Letter I" href="http://www.flickr.com/photos/49968232@N00/2241943956"><IMG alt="Pewter Uppercase Letter I" src="http://static.flickr.com/2363/2241943956_82a2e451d6_s.jpg" border=0></A> <A id=fs_10 title=N href="http://www.flickr.com/photos/49968232@N00/2586837312"><IMG alt=N src="http://static.flickr.com/3064/2586837312_d6f8a346c0_s.jpg" border=0></A> <A id=fs_11 title=G href="http://www.flickr.com/photos/34427470616@N01/2458250976"><IMG alt=G src="http://static.flickr.com/2232/2458250976_7275a7d5d9_s.jpg" border=0></A>

常用链接

留言簿(2)

随笔档案

文章分类

文章档案

收藏夹

java

搜索

  •  

最新评论

  • 1. re: 实现 Java 多线程并发控制框架
  • 不知道您看懂了这篇文章了没有?反正我是看不懂,不知道你能不能实现份代码?如果能的话能话,能不能给我一份?谢谢啦 junmingabc@gmail.com
  • --小陈
  • 2. re: ThreadLocal与synchronize
  • 之所以用不同的对象作为锁,是希望每一个同步块同时都只能被一个线程所访问,而不是所有同步块只能被一个线程所访问,这是我的理解,不知道是否正确。
  • --rainman

阅读排行榜

评论排行榜

再谈ReentrantLock

入锁(ReentrantLock)是一种递归无阻塞的同步机制。以前一直认为它是synchronized的简单替代,而且实现机制也不相差太远。不过最近实践过程中发现它们之间还是有着天壤之别。

以下是官方说明:一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。

它提供了lock()方法:
如果该锁定没有被另一个线程保持,则获取该锁定并立即返回,将锁定的保持计数设置为 1。
如果当前线程已经保持该锁定,则将保持计数加 1,并且该方法立即返回。
如果该锁定被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁定之前,该线程将一直处于休眠状态,此时锁定保持计数被设置为 1。

最近在研究Java concurrent中关于任务调度的实现时,读了延迟队列DelayQueue的一些代码,比如take()。该方法的主要功能是从优先队列(PriorityQueue)取出一个最应该执行的任务(最优值),如果该任务的预订执行时间未到,则需要wait这段时间差。反之,如果时间到了,则返回该任务。而offer()方法是将一个任务添加到该队列中。

后来产生了一个疑问:如果最应该执行的任务是一个小时后执行的,而此时需要提交一个10秒后执行的任务,会出现什么状况?还是先看看take()的源代码:

public E take() throws InterruptedException {

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

for (;;) {

E first
= q.peek();

if (first == null) {

available.await();

}
else {

long delay = first.getDelay(TimeUnit.NANOSECONDS);

if (delay > 0) {

long tl = available.awaitNanos(delay);

}
else {

E x
= q.poll();

assert x != null;

if (q.size() != 0)

available.signalAll();
// wake up other takers

return x;

}

}

}

}
finally {

lock.unlock();

}

}

而以下是offer()的源代码:

public boolean offer(E e) {

final ReentrantLock lock = this.lock;

lock.lock();

try {

E first
= q.peek();

q.offer(e);

if (first == null || e.compareTo(first) < 0)

available.signalAll();

return true;

}
finally {

lock.unlock();

}

}

如代码所示,take()和offer()都是lock了重入锁。如果按照synchronized的思维(使用诸如synchronized(obj)的方法),这两个方法是互斥的。回到刚才的疑问,take()方法需要等待1个小时才能返回,而offer()需要马上提交一个10秒后运行的任务,会不会一直等待take()返回后才能提交呢?答案是否定的,通过编写验证代码也说明了这一点。这让我对重入锁有了更大的兴趣,它确实是一个无阻塞的锁。

下面的代码也许能说明问题:运行了4个线程,每一次运行前打印lock的当前状态。运行后都要等待5秒钟。

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

final ExecutorService exec = Executors.newFixedThreadPool(4);

final ReentrantLock lock = new ReentrantLock();

final Condition con = lock.newCondition();

final int time = 5;

final Runnable add = new Runnable() {

public void run() {

System.out.println(
"Pre " + lock);

lock.lock();

try {

con.await(time, TimeUnit.SECONDS);

}
catch (InterruptedException e) {

e.printStackTrace();

}
finally {

System.out.println(
"Post " + lock.toString());

lock.unlock();

}

}

};

for(int index = 0; index < 4; index++)

exec.submit(add);

exec.shutdown();

}

 

这是它的输出:
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]

每一个线程的锁状态都是“Unlocked”,所以都可以运行。但在把con.await改成Thread.sleep(5000)时,输出就变成了:
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]

以上的对比说明线程在等待时(con.await),已经不在拥有(keep)该锁了,所以其他线程就可以获得重入锁了。

有必要会过头再看看Java官方的解释:“如果该锁定被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁定之前,该线程将一直处于休眠状态”。我对这里的“保持”的理解是指非wait状态外的所有状态,比如线程Sleep、for循环等一切有CPU参与的活动。一旦线程进入wait状态后,它就不再keep这个锁了,其他线程就可以获得该锁;当该线程被唤醒(触发信号或者timeout)后,就接着执行,会重新“保持”锁,当然前提依然是其他线程已经不再“保持”了该重入锁。

总结一句话:对于重入锁而言,"lock"和"keep"是两个不同的概念。lock了锁,不一定keep锁,但keep了锁一定已经lock了锁。

posted on 2008-10-03 17:55 rainman 阅读(3956) 评论(0)  编辑  收藏 所属分类: java多线程


只有注册用户登录后才能发表评论。


网站导航: