ReentrantLock 与 synchronized对比

最近有在阅读Java并发编程实战这本书,又看到了ReentrantLock和synchronized的对比,发现自己以前对于RenntrantLock的理解很片面,特此做一番总结,如果有总结不到位的,欢迎指出

java.util.concurrent.locks 
接口 Lock

所有已知实现类:
ReentrantLockReentrantReadWriteLock.ReadLockReentrantReadWriteLock.WriteLock

为什么需要Lock?

java.util.concurrent.locks 
接口 Lock

所有已知实现类:
ReentrantLockReentrantReadWriteLock.ReadLockReentrantReadWriteLock.WriteLock

Lock提供了一种如条件的、可轮询的、定时的以及可以终端的获取锁的操作,所有的加锁方式和解锁方式都是显式的。

public class Lock {
private boolean locked = false; public Lock() {
} public final synchronized void lock() throws InterruptedException {
while(this.locked) {
this.wait();
} this.locked = true;
} public final synchronized void unlock() {
this.locked = false;
this.notifyAll();
}
}

Lock是JAVA5.0出现的,它的出现并不是为了替代synchronized,而是在synchronized不适用的时候使用。

那么synchronized有什么局限性呢?

  • 无法中断一个正在获取锁的线程

当一个线程想获取已经被其他线程持有的锁时,就会发生堵塞,假设已经持有锁的线程一直不释放锁,那么线程就会一直等待下去。

  • 无法指定获得锁的等待时间

比如,想要A线程执行某个操作,想在指定时间内A线程没有获取到锁就返回。synchronized是做不到的。

相同点:

  • 独占锁: 一次只允许一个线程访问
  • 可重入锁: 一个线程可重复获得自己已获得锁,不会发生死锁。简单来说,递归的时候不会发生死锁

不同点:

  • Lock不是java内置的,synchronized是JVM内置的,因此是内置特性。
  • 释放锁的方式:
    • Lock 必须要在finally中手动释放锁
    • synchronized 会根据锁区域代码自动执行完毕,或者发生异常,JVM会自动释放锁
  • 公平:
    • Lock是可公平可不公平锁
    • synchronized是不公平锁        

ReentrantLock的使用:

基本使用 

Lock lock = new ReentrantLock();
lock.lock();
try {
//....
} finally {
lock.unlock();
}
  • lock: 调用后一直阻塞直到获得锁。
package com.amber;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class TestReentrantLock {
Lock lock = new ReentrantLock(); public static void main(String[] args) {
TestReentrantLock testReentrantLock = new TestReentrantLock();
new Thread(() -> {
try {
testReentrantLock.testConcurrency(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程1").start(); new Thread(() -> {
try {
testReentrantLock.testConcurrency(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程2").start();
} private void testConcurrency(Thread thread) throws InterruptedException {
//获取锁成功返回true,如果获取失败,等待2S,规定时间内还是没有获得锁,那么就返回false
if (lock.tryLock( 2000, TimeUnit.MICROSECONDS)) {
try {
System.out.println(thread.getName() + " : " + "获取锁");
Thread.sleep(3000);
} finally {
System.out.println(thread.getName() + "释放锁");
lock.unlock(); //一定记得要释放锁
}
} else {
System.out.println(Thread.currentThread().getName() + " : " + "等待了,没有获取锁");
}
}
}
  • tryLock:拿到锁返回true,否则false;带有时间限制的tryLock(long time, TimeUnit timeUnit),拿不到锁,就等待一段时间,超时返回false
  • lockInterruptibly :调用后如果没有获取到锁会一直阻塞,阻塞过程中会接受中断信号。
 lockInterruptibly有点难以理解,假设A线程想去获取锁,但是锁被B线程持有,那么A就会发生堵塞。
A堵塞的时候,可以有以下两种方法发生状态改变:
  1. A获取锁资源
  2. A被其他线程中断:
    1. 这里只得被其他线程中断的意思是,C线程调用A线程的interrupt()。那么此时A线程就会被唤醒,处理中断信号。

  lockInterruptibly是被中断,就由阻塞状态被唤醒去处理中断信号。

在JAVA并发编程实战这本书中还提到了ReentrantLock的一个重要用法,那就是轮询锁。下面是书中的源代码:

 public boolean transferMoney(Account fromAcct, Account toAcct, DollarAmount amount,  long timeout, TimeUnit unit) throws InsufficientFundsException, InterruptedException {
long fixedDelay = 1;
long randMod = 2;
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);
returntrue;
}
} finally {
toAcct.lock.unlock();
}
}
} finally {
fromAcct.lock.unlock();
}
}
if (System.nanoTime() < stopTime)
returnfalse;
NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
}
}

程序发生死锁的时候,往往只能通过重新启动程序解决。而有时候因为获取锁的时序不一致,很容易发生死锁。根据上述代码第6行和第8行,假设此时我们使用的synochronized内置锁,A线程从cc账号转账到dd账号,B线程从dd账号转账到cc账号,就很容易发生死锁。但是使用tryLock()却可以避免锁顺序造成死锁的问题,

如果线程A、B不能同时获取cc和dd对象的锁,那么就会放弃自己已经获得的锁。

JUC - ReentrantLock 的基本用法 以及 lock()、tryLock()、lockInterruptibly()的区别的更多相关文章

  1. ReentrantLock可重入锁lock,tryLock的区别

    void lock(); Acquires the lock. Acquires the lock if it is not held by another thread and returns im ...

  2. java并发-ReentrantLock的lock和lockInterruptibly的区别

    ReentrantLock的加锁方法Lock()提供了无条件地轮询获取锁的方式,lockInterruptibly()提供了可中断的锁获取方式.这两个方法的区别在哪里呢?通过分析源码可以知道lock方 ...

  3. 【JDK1.8】JUC——ReentrantLock

    一.前言 在之前的几篇中,我们回顾了锁框架中比较重要的几个类,他们为实现同步提供了基础支持,从现在开始到后面,就开始利用之前的几个类来进行各种锁的具体实现.今天来一起看下ReentrantLock,首 ...

  4. java高并发系列 - 第12天JUC:ReentrantLock重入锁

    java高并发系列 - 第12天JUC:ReentrantLock重入锁 本篇文章开始将juc中常用的一些类,估计会有十来篇. synchronized的局限性 synchronized是java内置 ...

  5. Java同步锁——lock与synchronized 的区别【转】

    在网上看来很多关于同步锁的博文,记录下来方便以后阅读 一.Lock和synchronized有以下几点不同: 1)Lock是一个接口,而synchronized是Java中的关键字,synchroni ...

  6. Lock和synchronized的区别和使用

    Java并发编程:Lock 今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉,有许多疑问,所有在网上找了很多文章看了一下,总体说的不够详细,重点和焦点问 ...

  7. java并发之Lock以及和synchronized区别

    从Java5之后,在Java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 1.Lock 首先要说明的就是Lock,通过查看Lock的源码可知,Lo ...

  8. 002 Lock和synchronized的区别和使用

    转自 https://www.cnblogs.com/baizhanshi/p/6419268.html 今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟 ...

  9. Lock和synchronized的区别和使用(转发)

    今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉,有许多疑问,所有在网上找了很多文章看了一下,总体说的不够详细,重点和焦点问题没有谈到,但这篇文章相当不 ...

随机推荐

  1. 【linux】【FastDFS】FastDFS上传返回的url直接下载和下载文件的文件名问题

    FastDFS安装及其他问题参考:https://www.cnblogs.com/jxd283465/p/11556263.html直接调用FastDFS返回的url,浏览器访问后默认打开方式./us ...

  2. Codeforces New Year and Buggy Bot 题解

    主要思路:全排列,然后按输入的字符串走并且判断是否撞墙 注:这样不会TLE,全排列最多24种 Code(C++): #include<bits/stdc++.h> using namesp ...

  3. 使用uEdit时,在线管理图片功能不可用

    把所有的配置文件都配置好了,uedit的在线管理功能图片还是不可用,看了一下源码: 是的,它在img的src属性后边的图片上加上了参数,那肯定不能用啊,所以修改源文件image.js.直接搜索noCa ...

  4. Java 世界的盘古和女娲 —— Zygote

    本文基于 Android 9.0 , 代码仓库地址 : android_9.0.0_r45 文中源码链接: Zygote.java ZygoteInit.java ZygoteServer.java ...

  5. 关于json字符串与实体之间的严格验证

    在一个项目中要求严格验证传入的json字符串与定义的 类匹配,否则不记录.感觉这个严格验证找了好多资料才找到,可能用的人比较少,特摘出来给大家分析,直接上代码了:   using Newtonsoft ...

  6. Spring MVC-从零开始-view-向页面传递data(ModelAndView与ModelMap的区别)

    1.ModelMap的用法 package com.jt; import org.springframework.stereotype.Controller; import org.springfra ...

  7. 1.python环境配置 - python基础入门

    工欲善其事必先利其器,python学习首先要做得就是配置python环境.配置环境只需要下载Pycharm 和 Anaconda两个安装包即可,请跟上我得步伐,一步一步操作. 重要的事情说三遍: 先安 ...

  8. python 对excel进行截图

    工作中需要对excel的单元格区域进行截图,以前是调用vba进行(走了很多弯路,虽然能实现,但比较low),后来逐步发现python的win32com与vba师出同门,很多方法操作都是类似的. 可以对 ...

  9. Docker 环境搭建(RedHat 7)

    Docker 环境搭建(RedHat 7): CentOS7 下载 http://mirrors.sohu.com/centos/7/isos/x86_64/ 装载镜像文件 安装Linux 7, 参考 ...

  10. Linux 下复制整个文件夹的命令

    在 Linux 下复制整个文件夹,包括它的子文件夹及其隐藏文件的方法是: cp -r /etc/skel /home/user 或者 mkdir /home/<new_user> cp - ...