`
donlianli
  • 浏览: 340941 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Group-logo
Elasticsearch...
浏览量:218841
社区版块
存档分类
最新评论

慎用ReentrantLock

 
阅读更多
前言:
代码简洁与性能高效无法两全其美,本文章专注于并发程序的性能,如果您追求代码简洁,本文章可能不太适合,本文章属于Java Concurrency in Practice读书笔记。
在java5中,新增加ReentrantLock提供了一种比synchronized更为灵活的锁机制。为啥说灵活,而不是说性能更高呢?ReentrantLock提供的锁功能跟synchronized的功能基本是一致的,就是一翻版的synchronized类。但是它支持可轮询,定时及可中断的机制,所以说它是更灵活的。为啥没说他性能更高呢?因为这个在java6及以上,性能跟synchronized的基本持平。所以说,如果你的程序运行在java6以上,那么就没有必要再使用ReentrantLock对象了。
下面我们来仔细认识一下ReentrantLock。首先,他的正确用法:
Lock lock = new ReentrantLock();
...
lock.lock();
try {
    // 对锁定对象进行更新等操作
    //处理异常
} finally {
    lock.unlock();
}
 可以明显看出,它相对以下代码(synchronized写法,行数要多,控制要复杂。
synchronized(this){
	//更新操作
}
 那么,ReentrantLock被保留了下来,与synchronied相比还有什么优势呢?
1、可轮询。
原书上面的例子看着比较复杂,但意思很简单。一个转账的操作,要么在规定的时间内完成,要么在规定的时间内告诉调用者,操作没有完成。这个例子就是要了ReentrantLock的可轮询特性,就是在规定的时间内,反复去试图获得一个锁,如果获得成功,就能完成转账操作,如果在规定的时间内,没有获得这个锁,那么就是转账失败。如果使用synchronized的话,肯定是无法做到的。代码:
public boolean transferMoney(Account fromAcct,
                             Account toAcct,
                             DollarAmount amount,
                             long timeout,
                             TimeUnit unit)
        throws InsufficientFundsException, InterruptedException {
    long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
    long randMod = getRandomDelayModulusNanos(timeout, unit);
    long stopTime = System.nanoTime() + unit.toNanos(timeout);

    while (true) {
        if (fromAcct.lock.tryLock()) {
            try {
                if (toAcct.lock.tryLock()) {
                    try {
                        if (fromAcct.getBalance().compareTo(amount)
                                < 0)
                            throw new InsufficientFundsException();
                        else {
                            fromAcct.debit(amount);
                            toAcct.credit(amount);
                            return true;
                        }
                    } finally {
                        toAcct.lock.unlock();
                    }
                 }
             } finally {
                 fromAcct.lock.unlock();
             }
         }
         if (System.nanoTime() < stopTime)
             return false;
         NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
     }
}
 2、可中断
在synchronied的代码中,进入临界区的代码是无法中断的,这个很不灵活,如果我们使用一个线程池来分发任务,如果一个代码长期占有锁肯定会影响到线程池的其他任务,因此,加入中断机制提高了对任务更强的控制性。
public boolean sendOnSharedLine(String message)
        throws InterruptedException {
    lock.lockInterruptibly();
    try {
        return cancellableSendOnSharedLine(message);
    } finally {
        lock.unlock();
    }
}

private boolean cancellableSendOnSharedLine(String message)
    throws InterruptedException { ... }
 
在讨论到锁的时候,顺便说一下公平性。在new ReentrantLock的时候,有一个构造函数是带boolean类型的。这个参数告诉ReentrantLock是构造一个公平的锁还是不公平的锁。心想,获得一个锁还要指定公平性,我当然希望使用公平的锁了。
后来才明白,原来这里的公平性是指获取锁的时候,是否允许插队。允许插队,就是创建了不公平的锁。并且,ReentrantLock默认采用的是不公平的锁。为啥采用不公平的锁呢?应该先到先得嘛。原因在于线程挂起。当多个线程同时请求一个锁时,未获得锁的线程B会被挂起,当锁被线程A释放时,刚好来了一个线程C,那么操作系统就需要选择,第一,从挂起的队列中选择一个线程B,按照先到先得的原则,将锁交给它。但是这需要很大的开销,因为那个线程B很可能正睡觉呢,或者还在做美梦呢,叫醒它还得让它热热身,等他来接锁的时候,可能黄花菜都凉了。第二种选择,就是将锁交给刚好到来的这个线程C,刚到的线C程拿到锁就能使用。为了提高性能,操作系统选择第二个选择。
说到公平性,JDK的synchronized锁也是采用的非公平锁。
综合以上认识,如果你没有要求锁有可轮询和可中断的需求,还是使用synchronized内置锁吧。
 
 
对这类话题感兴趣?欢迎发送邮件至donlianli@126.com
关于我:邯郸人,擅长Java,Javascript,Extjs,oracle sql。
更多我之前的文章,可以访问 我的空间

 

1
4
分享到:
评论
11 楼 yixiandave 2013-07-25  
yixiandave 写道
teasp 写道
yixiandave 写道
如果要求多线程条件下多个方法同时只能有一个执行就只能用lock了吧
例如
public class Foo{
   public void a(){}
   public void b(){}
   public void c(){}
}

现要求abc3个方法只要任何一个执行那么其他调用都必须等待

每个都加synchronized不就可以了吗?

每个都加synchronized关键字只能保证这个方法同时只有一个线程调用
例如线程A调用了a()方法,此时线程B调用a()方法就必须等待线程A把a()方法执行完成。但是调用b()方法不受影响

抱歉我想当然了,经过测试synchronized在这种情况下有效
10 楼 yixiandave 2013-07-24  
teasp 写道
yixiandave 写道
如果要求多线程条件下多个方法同时只能有一个执行就只能用lock了吧
例如
public class Foo{
   public void a(){}
   public void b(){}
   public void c(){}
}

现要求abc3个方法只要任何一个执行那么其他调用都必须等待

每个都加synchronized不就可以了吗?

每个都加synchronized关键字只能保证这个方法同时只有一个线程调用
例如线程A调用了a()方法,此时线程B调用a()方法就必须等待线程A把a()方法执行完成。但是调用b()方法不受影响
9 楼 teasp 2013-07-24  
yixiandave 写道
如果要求多线程条件下多个方法同时只能有一个执行就只能用lock了吧
例如
public class Foo{
   public void a(){}
   public void b(){}
   public void c(){}
}

现要求abc3个方法只要任何一个执行那么其他调用都必须等待

每个都加synchronized不就可以了吗?
8 楼 yixiandave 2013-07-24  
如果要求多线程条件下多个方法同时只能有一个执行就只能用lock了吧
例如
public class Foo{
   public void a(){}
   public void b(){}
   public void c(){}
}

现要求abc3个方法只要任何一个执行那么其他调用都必须等待
7 楼 teasp 2013-07-23  
ahyyxx222 写道
难道没人觉得ReentrantLock最大的好处是减少了锁的范围,更加可控吗

ReentrantLock怎么减少了锁的范围?举个例子对比下看看。
6 楼 donlianli 2013-07-22  
syd6815892 写道
有自己的观点,可惜代码是抄袭的, 现在什么都抄,代码也抄,游戏啊,歌啊,代码也是,什么时候这些都不抄了,天朝就强大了。

哈哈,这个代码没啥意义,本来就是读书笔记。
5 楼 ahyyxx222 2013-07-22  
难道没人觉得ReentrantLock最大的好处是减少了锁的范围,更加可控吗
4 楼 syd6815892 2013-07-22  
有自己的观点,可惜代码是抄袭的, 现在什么都抄,代码也抄,游戏啊,歌啊,代码也是,什么时候这些都不抄了,天朝就强大了。
3 楼 须等待 2013-07-22  
teasp 写道
ReentrantLock和内置锁实现原理都不一样,怎么可能是内置锁的翻版?


我们更多关注效率,根据书上的说法是效率差不多
2 楼 teasp 2013-07-22  
ReentrantLock和内置锁实现原理都不一样,怎么可能是内置锁的翻版?
1 楼 liangcoder 2013-07-21  
拜读了 有点意思:)

相关推荐

    Java并发之ReentrantLock类源码解析

    Java并发之ReentrantLock类源码解析 ReentrantLock是Java并发包中的一种同步工具,它可以实现可重入锁的功能。ReentrantLock类的源码分析对理解Java并发机制非常重要。本文将对ReentrantLock类的源码进行详细分析,...

    java ReentrantLock详解.docx

    `ReentrantLock`是Java并发编程中的一种高级锁机制,它是`java.util.concurrent.locks`包中的类,提供了比`synchronized`关键字更丰富的功能和更细粒度的控制。相较于`synchronized`,`ReentrantLock`的主要优势在于...

    ReentrantLock解析

    《ReentrantLock深度解析》 在Java并发编程中,ReentrantLock是JDK提供的一个可重入互斥锁,它是java.util.concurrent.locks包下的核心类。与synchronized关键字相比,ReentrantLock提供了更高的灵活性,如尝试加锁...

    ReentrantLock与synchronized区别

    java语言 并发编程 ReentrantLock与synchronized区别 详解

    Java中ReentrantLock的使用.docx

    Java中的ReentrantLock是线程安全编程中的一种高级锁机制,它属于Lock接口的一个实现,提供了比synchronized更丰富的功能和更高的灵活性。ReentrantLock的名字来源于它的可重入性,这意味着一个线程可以多次获取同一...

    Lock、Synchoronized和ReentrantLock的使用

    此外,它还提供了在激烈争用情况下更佳的性能。 八、Reentrant 锁 Reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加 1,然后锁需要被释放...

    ReentrantLock源码的使用问题详解.docx

    《ReentrantLock源码详解与应用》 ReentrantLock,可重入锁,是Java并发编程中一个重要的锁实现,它提供了比synchronized更高级别的控制能力,包括公平性和非公平性选择。本文将深入探讨ReentrantLock的原理,特别...

    ReentrantLock与synchronized

    在Java多线程编程中,`ReentrantLock`和`synchronized`都是用于实现线程同步的重要工具,确保在并发环境中数据的一致性和正确性。两者虽然都能实现互斥访问,但在功能、性能以及使用场景上有所不同。下面我们将深入...

    ReentrantLock的使用及注意事项

    ReentrantLock的使用及注意事项

    ReentrantLock源码分析

    ### ReentrantLock源码分析 #### 一、ReentrantLock简介 ReentrantLock是一个基于`AbstractQueuedSynchronizer`(AQS)实现的高级锁工具类。与传统的synchronized关键字相比,ReentrantLock提供了更多控制手段,比如...

    Java多线程之ReentrantLock与Condition - 平凡希 - 博客园1

    Java中的`ReentrantLock`是Java并发包`java.util.concurrent.locks`中的一个高级锁机制,它是可重入的互斥锁,具有与`synchronized`关键字相似的同步性,但提供了更多的灵活性和控制功能。本篇文章将深入探讨`...

    ReentrantLock 实现原理 1

    ReentrantLock 实现原理详解 ReentrantLock 是 Java 中的一个同步工具类,它实现了 Lock 接口,提供了锁的获取和释放机制。ReentrantLock 的实现原理基于 AQS(AbstractQueuedSynchronizer),是一个重入锁,允许一...

    一张图将整个ReentrantLock流程看懂

    一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个...

    ReentrantLock代码剖析之ReentrantLock_lock

    `ReentrantLock`是Java并发编程中非常重要的一种锁,它提供了比`synchronized`关键字更细粒度的控制,并且在高竞争条件下的性能更优。在本文中,我们将深入分析`ReentrantLock`的`lock()`方法,理解其内部机制,包括...

    ReentrantLock lock方法注释

    ReentrantLock lock方法注释

    教你完全理解ReentrantLock重入锁

    1. ReentrantLock的介绍 ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java...

    ReentrantLock 与 synchronized 简介

    ### ReentrantLock 与 synchronized 的比较 #### 一、引言 在Java中,多线程和并发控制一直是程序员关注的重点。随着Java的发展,其语言本身及标准库提供了丰富的工具来帮助开发者处理并发问题。其中,`...

    使用ReentrantLock和Lambda表达式让同步更

    在高级并发编程中,`ReentrantLock`是一个强大的工具,相较于内置的`synchronized`关键字,它提供了更多的灵活性和控制。本篇文章将深入探讨`ReentrantLock`的使用以及如何结合Lambda表达式来优化同步代码。 `...

    locks框架_ReentrantLock.pdf

    ReentrantLock作为Lock接口的一个实现,是这个框架中的核心组件,尤其值得关注。它的主要特点是可重入性,这意味着一个线程可以多次获取同一锁,从而避免了死锁的风险。 ReentrantLock内部依赖于...

    ReentrantLock.java

    ReentrantLock.java

Global site tag (gtag.js) - Google Analytics