转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543947.html 

我们知道,线程安全问题需要通过线程之间的同步来解决,而同步大多使用syncrhoized关键字,简单方便。但是syncrhoized功能上较单一,为此,concurrent包为我们提供了额外的几种同步控制工具,让我们可以根据不同的同步需求更加灵活地选择同步工具。

一:ReentrantLock(重入锁)

ReentrantLock具有可重入、可中断、可限时申请、可公平获取等特点。

它的使用很简单:1:创建一个reentrantlock对象;2:在需要同步控制的代码块的起始处用 lock.lock() 加锁;3:在退出同步控制块处  lock.unlock() 解锁。

1)可重入

ReentrantLock可以重复获得同一把锁,即:在一个线程中,一个reentrantlock对象可以重复调用 lock() 申请这个锁对象进行加锁。

    我们知道。如果一个线程申请同步锁时,如果锁被占用了就会导致阻塞,等待请求的锁释放,若是请求的锁刚好是自己以及在使用的锁呢?难道也要导致阻塞吗?可重入性就是为了解决这个问题,若当前线程请求的锁是自己已获得的锁,则使锁的获取计数器+1而已,不会自己阻塞自己,线程继续照常执行。

但是可重入性也要求了,一个线程请求了多少次同一个锁,那么就要相应地在结束时释放多少次。

lock.lock();
lock.lock();
try
{
i++; }
finally
{
lock.unlock();
lock.unlock();
}

2)可中断

如果有两个线程,一个在lock1.lock()后申请了lock2.lock(),一个在lock2.lock()后申请了lock1.lock(),启动这两个线程。这两个线程都在等待对方释放掉已申请的lock,好让自己获得第二个lock而进行下去,这样的话就会导致死锁。我们知道死锁是由于资源请求存在环路导致的,只要打破这个环路即可让大部分请求得到满足,打破就是我们说的——中断。我们可以通过中断其中一个线程,使其让出所占用的资源(已获得的lock),则另一个线程即可获得两个lock而得以继续进行下去。

reentrantlock的可中断锁是通过 lockInterruptibly() 获取的,而不是普通的 lock()方法。

public static ReentrantLock lock1 = new ReentrantLock();

lock1.lockInterruptibly();
            

我们怎么知道死锁并解决呢?这需要用到ThreadMXBean(Java虚拟机线程管理接口)中提供的一系列方法,我们可以通过 ManagementFactory .getThreadMXBean();来获得一个线程管理接口的实现类,里面实现了ThreadMXBean中的线程管理方法,罗列如下:

方法摘要
 ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers) 
          返回所有活动线程的线程信息,并带有堆栈跟踪和同步信息。
 long[] findDeadlockedThreads() 
          查找因为等待获得对象监视器或可拥有同步器而处于死锁状态的线程循环。
 long[] findMonitorDeadlockedThreads() 
          找到处于死锁状态(等待获取对象监视器)的线程的周期。
 long[] getAllThreadIds() 
          返回活动线程 ID。
 long getCurrentThreadCpuTime() 
          返回当前线程的总 CPU 时间(以毫微秒为单位)。
 long getCurrentThreadUserTime() 
          返回当前线程在用户模式中执行的 CPU 时间(以毫微秒为单位)。
 int getDaemonThreadCount() 
          返回活动守护线程的当前数目。
 int getPeakThreadCount() 
          返回自从 Java 虚拟机启动或峰值重置以来峰值活动线程计数。
 int getThreadCount() 
          返回活动线程的当前数目,包括守护线程和非守护线程。
 long getThreadCpuTime(long id) 
          返回指定 ID 的线程的总 CPU 时间(以毫微秒为单位)。
 ThreadInfo getThreadInfo(long id) 
          返回指定 id 的不具有堆栈跟踪的线程的线程信息。
 ThreadInfo[] getThreadInfo(long[] ids) 
          返回其 ID 在输出数组 ids 中的每个线程的线程信息,这些线程不具有堆栈跟踪。
 ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers) 
          返回每个线程的线程信息,线程 ID 位于输入数组 ids 中,带有堆栈跟踪和同步信息。
 ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) 
          返回其 ID 在输入数组 ids 中的每个线程的线程信息,并带有指定堆栈追踪元素数的堆栈追踪。
 ThreadInfo getThreadInfo(long id, int maxDepth) 
          返回指定 id 的线程的线程信息,并带有指定堆栈追踪元素数的堆栈追踪。
 long getThreadUserTime(long id) 
          返回指定 ID 的线程在用户模式中执行的 CPU 时间(以毫微秒为单位)。
 long getTotalStartedThreadCount() 
          返回自从 Java 虚拟机启动以来创建和启动的线程总数目。
 boolean isCurrentThreadCpuTimeSupported() 
          测试 Java 虚拟机是否支持当前线程的 CPU 时间测量。
 boolean isObjectMonitorUsageSupported() 
          测试 Java 虚拟机是否支持使用对象监视器的监视。
 boolean isSynchronizerUsageSupported() 
          测试 Java 虚拟机是否支持使用可拥有同步器的监视。
 boolean isThreadContentionMonitoringEnabled() 
          测试是否启用了线程争用监视。
 boolean isThreadContentionMonitoringSupported() 
          测试 Java 虚拟机是否支持线程争用监视。
 boolean isThreadCpuTimeEnabled() 
          测试是否启用了线程 CPU 时间测量。
 boolean isThreadCpuTimeSupported() 
          测试 Java 虚拟机实现是否支持任何线程的 CPU 时间测量。
 void resetPeakThreadCount() 
          将峰值线程计数重置为当前活动线程的数量。
 void setThreadContentionMonitoringEnabled(boolean enable) 
          启用或禁用线程争用监视。
 void setThreadCpuTimeEnabled(boolean enable) 
          启用或禁用线程 CPU 时间测量。

从上面我们可以看到,通过ThreadMXBean我们可以获取虚拟机当前运行的所以线程的信息,包括 线程ID、存活的线程、死锁的线程等等信息。

所以,我们可以通过ThreadMXBean获取到由reentrantlock的lockInterruptibly()导致的死锁的随便一个线程的ID,然后通过 线程的interrupt() 方法自动中断自己,就可以使得死锁环中其他线程得以顺利执行。

3)可限时申请锁

reentrantlock对象可以限时申请获得锁,如果在限定时间内没能获取则放弃申请,在没锁的情况下执行代码(此时是不同步进行,线程不安全)。这样就不会由于申请被占用的锁而阻塞线程。

限时申请锁是通过 lock.trylock(num,unit)实现的,unit是时间单元,可以选择秒、分钟、小时、天等,num是数量,指定有多少个时间单元时间用来尝试获得锁。

public static ReentrantLock lock = new ReentrantLock();

lock.tryLock(5, TimeUnit.SECONDS);
            

4)可公平获取

一般的锁不是公平的,不会说先申请的线程就先获得锁,这样容易使得某一个线程一直处于申请获得锁的状态,即——饥饿。

ReentrantLock可以在创建时通过构造参数指定是不是公平锁,当作为公平锁创建时,申请获得该锁的线程们将严格按照“先申请先获得”的顺序来使用该锁。

public ReentrantLock(boolean fair) 

public static ReentrantLock fairLock = new ReentrantLock(true);

Java多线程之ReentrantLock重入锁简介与使用教程的更多相关文章

  1. java并发系列(三)-----ReentrantLock(重入锁)功能详解和应用演示

    1. ReentrantLock简介 jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock.虽然在性能上ReentrantLock和synchronize ...

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

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

  3. java并发系列(四)-----源码角度彻底理解ReentrantLock(重入锁)

    1.前言 ReentrantLock可以有公平锁和非公平锁的不同实现,只要在构造它的时候传入不同的布尔值,继续跟进下源码我们就能发现,关键在于实例化内部变量sync的方式不同,如下所示: /** * ...

  4. ReentrantLock(重入锁)以及公平性

    ReentrantLock(重入锁)以及公平性 标签(空格分隔): java NIO 如果在绝对时间上,先对锁进行获取的请求一定被先满足,那么这个锁是公平的,反之,是不公平的,也就是说等待时间最长的线 ...

  5. 从源码角度彻底理解ReentrantLock(重入锁)

    目录 1.前言 2.AbstractQueuedSynchronizer介绍 2.1 AQS是构建同步组件的基础 2.2 AQS的内部结构(ReentrantLock的语境下) 3 非公平模式加锁流程 ...

  6. ReentrantLock(重入锁)的源码解析

    转自:从源码角度彻底理解ReentrantLock(重入锁)](https://www.cnblogs.com/takumicx/p/9402021.html)) 公平锁内部是FairSync,非公平 ...

  7. Java多线程之ReentrantLock与Condition

    一.ReentrantLock 1.ReentrantLock简介 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”.ReentrantLock 类实现了 Lock ,它拥有与 sy ...

  8. ReentrantLock 重入锁(下)

    前沿:       ReentrantLock 是java重入锁一种实现,在java中我们通常使用ReentrantLock 和 synchronized来实现锁功能,本篇通过例子来理解下Reentr ...

  9. java多线程之ReentrantLock

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

随机推荐

  1. Oracle行列转换小结

    目录结构如下:行转列列转行[一].行转列 1.1.初始测试数据 表结构:TEST_TB_GRADE Sql代码 create table TEST_TB_GRADE (   ID        NUM ...

  2. 在linux机器上面安装anaconda和相关软件

    直接安装anaconda参考这里,主要两条命令: wget https://repo.continuum.io/archive/Anaconda3-5.0.1-Linux-x86_64.sh bash ...

  3. java问题排查命令

    java问题排查命令 jps:查看java进程 jmap:导出堆详细信息(与jhat一起使用) jhat:分析Java堆的命令(与jmap一起使用) jstack:可以定位到线程堆栈,根据堆栈信息我们 ...

  4. [leetcode]Palindrome Partitioning @ Python

    原题地址:https://oj.leetcode.com/problems/palindrome-partitioning/ 题意: Given a string s, partition s suc ...

  5. thinkphp生成的验证码不显示问题解决

    在调用验证码之前加上 ob_clean(); 不显示验证码的代码: public function verify(){ $verify = new \Think\Verify(); $verify-& ...

  6. js获取当前域名、Url、相对路径和参数

    用 Javascript 可以单独获取当前域名.Url.相对路径和参数,所谓单独攻取,即域名不包括网页文件的路径和参数.参数不包括域名和网页文件路径,下面分别介绍. 一.js获取当前域名有2种方法 1 ...

  7. js el jstl list 循环

    需要在js中获取从Controller传过来的list集合,通过循环遍历找到对应的值,赋值到指定input框中 刚开始做法: for (var h = 0; h < gradesize; h++ ...

  8. Ubuntu升级到18.04

    1.更新 sudo apt-get update 2.升级 sudo apt-get upgrade 3.检测最新版本 sudo update-manager -c -d 然后就会弹出窗口,提示ubu ...

  9. 【应用】R--判断类别型属性之间是否有相关性(相互之间是否独立)

    检验某学区所有在售房源中,小区与楼栋类别(低层:多层;小高层:高层)是否相关 导入数据: > house<- read.table("house_data.txt", ...

  10. CreateFont函数为什么改变不了字体?该怎么解决

    CreateFont函数为什么改变不了字体?CFont   *   f;             f   =   new   CFont;             f-> CreateFont( ...