Java中Synchronized与ReentrantLock的不同以及ReentrantLock的使用

首先,ReentrantLock是在Java1.5中被加入的,所以在之前的Java版本中是不存在 ReentrantLock的。ReentrantLock是Java并发包中非常有用的一个类,比如在ConcurrentHashMap中就用到了ReentrantLock。ReentrantLock类有两个重要的特征,其中一个是当我们尝试获取锁时,我们能够中断这个获取锁的行为;另一个是我们能够指定一个获取锁的等到时间。这两个特征对于构建高响应性以及可伸缩性的系统非常重要。简单的说,ReentrantLock继承了synchronized关键字的功能,但是又提供了更多控制锁的行为。以下源码均来自Java8
目录:
[toc]

什么是ReentrantLock

查看ReentrantLock的源代码,我们可以看到ReentrantLock实现了Lock接口以及java.io.Serializable接口。ReentrantLock是一种互斥锁,类似于Java关键字synchronized提供的隐式锁。但是,ReentrantLock提供了 一些扩展功能如公平性,这个功能能够用于为最长等待的线程提供锁定。获取锁是通过调用lock方法,直到线程调用了unlock方法才释放锁。当我们创建ReentrantLock时,可以提供一个公平参数。
ReentrantLock类似于隐式锁定提供了相同的可见性和顺序保证,也就是unlock方法一定发生在另外一个线程调用lock方法之前。

隐式锁:隐式获取锁,synchronized是它的代表,使用者不需要关心内部锁的获取和释放,锁的相关操作都由具体的关键字完成
显示锁:显示的获取锁,Lock是其代表,需要使用者在使用的时候显示地获取和释放锁

ReentrantLock和synchronized的区别

尽管ReentrantLock像隐式锁一定提供了相同的可见性和顺序保证,但是ReentrantLock提供了更多的功能并且在一些方面跟synchronized是不同的。就如前面所说,ReentrantLock与synchronized主要的区别是在获取锁的中断操作能力以及等待获取锁的时间上。
1. 公平性(fairness)。synchronized不支持公平性。一旦锁被释放了,任何线程都能获得锁,不能指定哪个线程能够优先获取锁;另一方面,通过在创建ReentrantLock实例的时候我们可以指定公平属性。在竞争锁的过程中,通过指定公平性,我们能够为最长等待线程(即等待此锁最长的线程)提供锁。
ReentrantLock有两个构造函数,如下代码,可见无参数的构造函数是非公平性的。
ReentrantLock构造函数
2. 第二个主要的区别是tryLock方法。ReentrantLock提供了tryLock方法,当且仅当锁是可用的并且没有被其他的线程持有tryLock方法才能获取此锁。这减轻了线程在等待锁时产生的阻塞。
3. ReentrantLock在等待获取锁的过程中提供了中断,即我们能够中断获取锁的过程。在synchronized中,一个线程在等到锁的过程中会一直阻塞,如果陷入了无限等待中,我们是没有办法控制这个过程的。而ReentrantLock提供了一个lockInterruptibly方法,当线程正在等待获取锁的过程中,我们能够用此方法来中断线程。同时我们也可以给tryLock指定一个超时时间,在这个时间范围内如果没有获得锁,则返回false。

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
  1. ReentrantLock提供了获取等到锁的所有线程,如getQueuedThreads()方法

ReentrantLock的优势

其实在上一个小节中已经部分说明ReentrantLock 的优势,这里做一个总结
1. 能够中断获取锁过程
2. 在等待锁时可以指定超时时间
3. 能够创建公平锁
4. 获取所有等待锁的线程
5. 可以灵活的尝试获取锁而不锁定线程

ReentrantLock的劣势

凡事都有两面性,说完ReentrantLock的优势,再来说说ReentrantLock的劣势。ReentrantLock的主要缺点是使用ReentrantLock时必须将ReentrantLock中的方法放到try-finally结构中,这就造成了代码不易读并且隐藏了业务逻辑。尽管类似于Eclipse及Netbeans的IDE能够为我们添加try catch块,但是代码仍然显得有点乱。另一个缺点是,使用ReentrantLock之后,类的获取以及释放全部由程序员负责,这虽然为我们编写代码提供了更多功能,但是也带来了新的bug,特别是我们忘记在finally块中释放锁。有点类似于C++中的free与delete一定要配对使用。

Java中使用ReentrantLock及Lock的例子

代码非常简单,就是两个线程分别修改count的值并且输出到标准输入上。getCount用ReentrantLock加锁,而getCountTwo用synchronized关键字加锁。从代码中可以看到采用synchronized关键字的代码更加易读,但是相对于ReentrantLock没有那么灵活。

package com.ll.concurrent;

import java.util.concurrent.locks.ReentrantLock;

/**
* @describe 如何使用ReentrantLock类
* @author liang
* @date 创建时间:2017年6月10日
*
*/
public class ReentrantLockHowto {
    //一个非公平的ReentrantLock
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    //在lock上获取锁,在finally中释放锁
    public int getCount() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " getCount方法获取count的值:" + count);
            return count++;
        } finally {
            lock.unlock();
        }
    }
    //采用synchronized隐式加锁
    public synchronized int getCountTwo() {
        System.out.println(Thread.currentThread().getName() + " getCountTwo方法获取count的值: " + count);
        return count++;
    }
    public static void main(String args[]) {
        final ReentrantLockHowto counter = new ReentrantLockHowto();
        Thread t1 = new Thread() {
            public void run() {
                while (counter.getCount() < 6) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t2 = new Thread() {
            public void run() {
                while (counter.getCount() < 6) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

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

代码在本人github,其中说明了java中一些类的使用方式,欢迎start。

参考链接

ReentrantLock Example in Java, Difference between synchronized vs ReentrantLock

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值