【概述】

重入锁可以完全代替synchronized关键字。

与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁对于逻辑控制的灵活性好于synchronized。

要注意的是,每次在退出临界区时,必须记得释放锁,否则其他线程将没有机会访问临界区了。

【ReentrantLock入门例子】

package com.higgin.reentrantLock;

import java.util.concurrent.locks.ReentrantLock;

/**
* Created by HigginCui on 2017/5/15.
*/
public class ReentrantLockThread implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); //实例化一个重入锁 public static int num = 0; @Override
public void run() {
for(int i =0; i<100; i++){
lock.lock(); //上锁
try{
num++;
}finally {
lock.unlock(); //释放锁
}
}
} public static void main(String[] args) throws InterruptedException{
ReentrantLockThread rlThread = new ReentrantLockThread();
Thread t1 = new Thread(rlThread);
Thread t2 = new Thread(rlThread); t1.start();
t2.start(); t1.join();
t2.join(); System.out.println(num);
}
}

【运行结果】

【为什么叫重入锁】

对于一个线程,这种锁是可以重复进入的。一个线程可以两次获得同一个锁。

有一点要注意,如果一个线程多次获得锁,那么释放锁必须有相同的次数。

如果释放锁的次数多了,会抛出一个java.lang.IllegalMonitorStateException异常。

如果释放锁的次数少了,相当于还持有这个锁,其他线程无法进入临界区。

lock.lock();
lock.lock(); //一个线程可以多次获得同一个锁
try{
num++;
}finally {
lock.unlock();
lock.unlock(); //锁必须释放相同的次数
}

【ReentrantLock的几个重要方法】

1.lock():获得锁,如果锁被占用,则等待。

2.lockInterruptibly():获得锁,但会优先响应中断。

3.tryLock():尝试获得锁,返回true/false,该方法不等待,立即返回。

4.tryLock(long time, Timeunit unit):在给定的时间内尝试获得锁。

5.unlock():释放锁。

【中断响应】

对于synchronized来说,如果一个线程在等待wait,那么结果只有两种情况:1.获得这把锁继续执行;2.继续保持等待。

对于重入锁,提供了第3种可能:3.线程还可以被中断。

 

【利用重入锁的中断响应来中断结束线程,解决死锁问题】

package com.higgin.reentrantLock;

import java.util.concurrent.locks.ReentrantLock;

/**
* Created by HigginCui on 2017/5/16.
*/
public class DeadLockThread implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); boolean flag; public DeadLockThread(boolean flag){
this.flag = flag;
} @Override
public void run() {
try {
if (flag == false) {
lock1.lockInterruptibly(); //获得lock1锁,优先响应中断,即在等待的过程中,可以响应中断
Thread.sleep();
lock2.lockInterruptibly();
} else {
lock2.lockInterruptibly(); //获得lcok2锁,优先响应中断,顺序与上面的相反
Thread.sleep();
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println(Thread.currentThread().getName()+"被中断啦!");
} finally {
if (lock1.isHeldByCurrentThread()) { //判断当前线程是否持有lock1锁
lock1.unlock(); //如果持有lock1锁,就释放
}
if(lock2.isHeldByCurrentThread()){ //再判断当前线程是否持有lock2锁,如果持有就释放
lock2.unlock();
}
System.out.println(Thread.currentThread().getName()+"线程退出啦!!");
}
} public static void main(String[] args) throws InterruptedException{
DeadLockThread d1 = new DeadLockThread(true);
DeadLockThread d2 = new DeadLockThread(false); Thread t1 = new Thread(d1,"t1");
Thread t2 = new Thread(d2,"t2"); t1.start();
t2.start(); //同时启动这两个线程,必定会产生死锁 Thread.sleep(); //main线程延时5秒
t2.interrupt(); //为t2线程产生一个中断,如果没有这个中断,两个线程都会处于死锁状态,都在等待对方释放锁
}
}

【运行结果】

【分析】

线程t1启动了,然后启动t2,

t1先占用lock1,等待500ms,

t2先占用lock2,等待500ms,

t1在500ms等待结束后,想获得lock2,但此时lock2已经被t2占用,

反之t2在500ms等待结束后,想获得lock1,但此时lcok1又被t1占用,

于是陷入了死锁状态。

由于lock.lcokInterruptibly()是一个可以对中断进行相应的锁申请动作,在线程等待的过程中,可以响应中断。

main线程在5000ms后中断了t2线程,那么t2会进入catch捕获异常,打印相关的异常信息,然后进入finally代码块释放当前线程t2持有的锁lock2,t2退出。

此时t1就可以获得t2释放的lock2,然后也进入finally代码块,释放其持有的两个锁lock1和lock2。

【申请锁设置等待时间tryLock(10,TimeUnit.SECONDS)】

这也是避免死锁的一种方法,即申请锁限时等待,给定一个等待时间,如果线程在这段时间内无法申请获得锁,那么线程会自动放弃。

【tryLock(long time, Timeunit unit)的例子】

package com.higgin.reentrantLock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; /**
* Created by HigginCui on 2017/5/17.
*/
public class TimeLockThread implements Runnable { ReentrantLock lock = new ReentrantLock(); @Override
public void run() {
try{
if (lock.tryLock(, TimeUnit.SECONDS)){ //如果当前线程5秒内无法获得对应的锁,那么其会自动释放锁
System.out.println(Thread.currentThread().getName()+"获得锁,并占用6秒!");
Thread.sleep(); //一旦获得锁,那么回占用锁的时间为6秒
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
if(lock.isHeldByCurrentThread()){
System.out.println(Thread.currentThread().getName()+"释放锁啦,然后退出!");
lock.unlock();
}else{
System.out.println(Thread.currentThread().getName()+"没有占用锁,直接退出!");
}
}
} public static void main(String[] args) {
TimeLockThread tt = new TimeLockThread();
Thread t1 = new Thread(tt,"t1");
Thread t2 = new Thread(tt,"t2"); t1.start();
t2.start();
}
}

【运行结果】

【分析】

本例中,占用锁的线程会持有锁6秒的时间,故另一个线程无法在5秒内获得锁,因此请求锁会失败。

如果tryLock( )不带参数,当前线程会尝试获得锁,如果锁并未被其他线程占用,则申请锁会成功,并立即返回true。

如果锁被其他线程占用,则当前线程不会进行等待,而是立即返回false。

tryLock()这种方式不会引起线程等待,因此也不会产生死锁。

【公平锁】

public ReentrantLock( boolean fair ); //fair=true,公平锁

公平锁会按照时间的顺序,保证先到者先获得锁,后到者后获得锁。公平锁最大的一个特点就是:不会产生饥饿现象。只要你排队,最终还是可以等到锁的。(如果使用synchronized关键字进行锁控制,那么产生的锁就是非公平的)

公平锁看起来很优美,但是其性能也非常低下,因此默认情况下,锁是非公平的,若没有特殊的需求,也不要使用公平锁。

17_重入锁ReentrantLock的更多相关文章

  1. synchronized关键字,Lock接口以及可重入锁ReentrantLock

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  2. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  3. 轻松学习java可重入锁(ReentrantLock)的实现原理

    转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...

  4. java 可重入锁ReentrantLock的介绍

    一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...

  5. 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  6. Java 显示锁 之 重入锁 ReentrantLock(七)

    ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...

  7. Java中可重入锁ReentrantLock原理剖析

    本文由码农网 – 吴极心原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 一. 概述 本文首先介绍Lock接口.ReentrantLock的类层次结构以及锁功能模板类AbstractQue ...

  8. Java多线程——深入重入锁ReentrantLock

    简述 ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”. ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychronized,是实现 ...

  9. java线程的同步控制--重入锁ReentrantLock

    我们常用的synchronized关键字是一种最简单的线程同步控制方法,它决定了一个线程是否可以访问临界区资源.同时Object.wait() 和Object.notify()方法起到了线程等待和通知 ...

随机推荐

  1. gym 102082G BZOJ4240 贪心+树状数组

    4240: 有趣的家庭菜园 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 756  Solved: 349[Submit][Status][Discu ...

  2. Maven-常规问题

    1.编译(compile)Maven项目时,报错: -Dmaven.multiModuleProjectDirectory system property is not set. 处理方案:Windo ...

  3. 转: centos系统home下的中文目录改为英文目录

    转自h t t p : / /xugang-1017-126-com.iteye.com/blog/2081845 如果安装了中文版的Cent OS之后,root目录和home目录下会出现中文的路径名 ...

  4. Python 时间格式转换

    Python time, datetime模块常用方法 1.使用time模块,获取当前时间戳~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~import timetime.time( ...

  5. Oracle 查询单挑语句运行时间

    pl/sql 不考虑网络IO等影响 declare guidname ):='255fc3db-815a-46c1-9187-ec0256305335'; timespa timestamp:=CUR ...

  6. PIXI 太空玉兔游戏(6)

    想法来源  出于练习看到这篇文章   没有什么难度  效果如下,接下来会用pixijs讲解如何实现 创建应用及舞台 HTML部分只创建标签引入 pixi.min.js  即可: <script ...

  7. how to use Sqoop to import/ export data

    Sqoop is a tool designed for efficiently transferring data between RDBMS and HDFS, we can import dat ...

  8. TOJ 2119 Tangled in Cables

    描述 You are the owner of SmallCableCo and have purchased the franchise rights for a small town. Unfor ...

  9. JSON转C#实体类

    https://www.bejson.com/convert/json2csharp/

  10. emacs使用笔记

    C-h t tutorial [移动基本操作]C-f C-b C-p C-n 前后上下 C-v C-a 行首 C-e行尾C-a 和 C-e 可以将光标移动到"一行"的头部和尾部.M ...