关于java中ReentrantLock类的源码分析以及总结与例子

一,官方描述



关于ReentrantLock的官方描述,英文的就不贴出来了,这里我只贴出我自己翻译的描述:

reentrant是一个跟synchronized具有相同行为和语义的持有锁来访问方法和语句的互斥锁,但是reentrant还拥有被扩展的能力。

ReentrantLock会被线程拥有并且持续锁定,不会解锁。线程调用lock()方法返回后,则成功持有锁,否则这个锁正在被另一个线程所持有,只能等待另一个线程释放锁,如果当前线程拥有了锁,则调用lock()方法会立即返回,这个状态可以通过isHeldByCurrentThread和getHoldCount方法。

这个类的构造器可以接受一个可选的参数,如果被设置为true,锁会准许线程等待最长的时间。另外,锁不会保证特别的访问顺序。程序被许多线程通过公平锁访问可能会降低整体的吞吐量,而不是使用默认的设置,而且有更小的差异及时获得锁以及确保不会存在饥饿。注意,无论如何,锁的公平性不保证线程计划的公平性。因此,许多线程使用公平锁也许在其他活动线程没有处理并且自己没有持有该锁的时候,其中的一个线程会接连的多次获得这个锁。还有要注意,不定期的tryLock方法不会遵守公平设置,如果这个锁是有效的甚至如果其他线程都在等待,他也将成功获得锁。

强烈建议在实践中总是在try块中立即调用lock方法,最典型的before/after结构如下:

class X {
    private final ReentrantLock lock = new ReentrantLock();
    public void run() {
        lock.lock();
        try {
        
        } finally {
            lock.unlock();
        }
    }
}

二,源码分析



    查看源码会发现,该类中主要又三个静态类来控制该类的功能,分别是Sync(超类),NonfairSync类,FairSync类,其中NonfairSync类是非公平类,它的作用是在有线程请求锁时,并不保证首先请求的线程可以拿到这个锁,而FairSync类是公平类,对于这个公平类,如果已经有线程持有锁了,那么请求线程会被放入队列中,然后按顺训请求锁。

在FairSync类的tryAcquire方法中,有一个if判断isFirst,这就是判断当前请求线程是否为队列中的第一个线程,如果是,则获取锁,如果不是,则跳过。

两者都有lock方法,但是实现却有一些不同,对于NonfairSync,其实现如下:

final void lock() {
           if (compareAndSetState(0, 1))   //关键在于这里。这里的意思是说,如果当前锁的状态如果是0,则就给这个线程锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
}

FairSync类的代码如下:

final void lock() {
    acquire(1);  //这里就是要通过队列来给线程分配锁
}

三,总结



    大多书情况下,大家可能都会选择使用synchronized来加锁,ReentrantLock确实是一种高级加锁工具,在确实需要一些 synchronized 所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。

四,例子


package test;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    private static final ReentrantLock lock = new ReentrantLock();
    private static final ReentrantLock fairlock = new ReentrantLock(true);
    private int n;
    public static void main(String[] args) {
        ReentrantLockTest rlt = new ReentrantLockTest();
        for(int i = 0; i < 100; i++) {
            Thread nonT = new Thread(new NonFairTestThread(rlt));
            nonT.setName("nonFair[" + (i + 1) + "]");
            nonT.start();
            
            Thread fairT = new Thread(new FairTestThread(rlt));
            fairT.setName("fair[" + (i + 1) + "]");
            fairT.start();
        }
    }
    //非公平锁
    static class NonFairTestThread implements Runnable {
        private ReentrantLockTest rlt;
        public NonFairTestThread(ReentrantLockTest rlt) {
            this.rlt = rlt;
        }
        
        public void run() {
            lock.lock();
            try {
                rlt.setNum(rlt.getNum() + 1);
                System.out.println(Thread.currentThread().getName() + " nonfairlock***************" + rlt.getNum());
            } finally {
                lock.unlock();
            }
            
        }
    }
    //公平锁
    static class FairTestThread implements Runnable {
        private ReentrantLockTest rlt;
        public FairTestThread(ReentrantLockTest rlt) {
            this.rlt = rlt;

        }
        
        public void run() {
            fairlock.lock();
            try {
                rlt.setNum(rlt.getNum() + 1);
                System.out.println(Thread.currentThread().getName() + "   fairlock=======" + rlt.getNum() + 
                        "   " + fairlock.getHoldCount() + " queuelength=" + fairlock.getQueueLength());
            } finally {
                fairlock.unlock();
            }
            
        }
    }
    
    public void setNum(int n) {
        this.n = n;
    }
    
    public int getNum() {
        return n;
    }
}

关于java中ReentrantLock类的源码分析以及总结与例子

时间: 2024-10-11 11:24:41

关于java中ReentrantLock类的源码分析以及总结与例子的相关文章

Java中arraylist和linkedlist源码分析与性能比较

Java中arraylist和linkedlist源码分析与性能比较 1,简介 在java开发中比较常用的数据结构是arraylist和linkedlist,本文主要从源码角度分析arraylist和linkedlist的性能. 2,arraylist源码分析 Arraylist底层的数据结构是一个对象数组,有一个size的成员变量标记数组中元素的个数,如下图: * The array buffer into which the elements of the ArrayList are sto

JDK中String类的源码分析(二)

1.startsWith(String prefix, int toffset)方法 包括startsWith(*),endsWith(*)方法,都是调用上述一个方法 1 public boolean startsWith(String prefix, int toffset) { 2 char ta[] = value; 3 int to = toffset; 4 char pa[] = prefix.value; 5 int po = 0; 6 int pc = prefix.value.l

【小白的java成长系列】——顶级类Object源码分析

首先来说一下api文档使用,api这个词对有一定开发经验的java编程人员来说是很喜爱的~ java当然也提供了api开发文档,下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 找到下面的: 下载自己喜爱的版本即可,解压,点击~/jdk-7u60-apidocs/api/index.html就可以查看其api了: 跟上网一样一样的,点击相应链接就可以查看其信息了. 进入正题,说说Object这个类: 先

ReentrantLock 与 AQS 源码分析

ReentrantLock 与 AQS 源码分析 1. 基本结构 ?? 重入锁 ReetrantLock,JDK 1.5新增的类,作用与synchronized关键字相当,但比synchronized更加灵活.ReetrantLock本身也是一种支持重进入的锁,即该锁可以支持一个线程对资源重复加锁,但是加锁多少次,就必须解锁多少次,这样才可以成功释放锁. 1. 继承 没有继承任何类,因为很多操作都使用了组合完成. 2. 实现 Lock, java.io.Serializable ??这里着重介绍

Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概念,主要讲了AQS的排队区是怎样实现的,什么是独占模式和共享模式以及如何理解结点的等待状态.理解并掌握这些内容是后续阅读AQS源码的关键,所以建议读者先看完我的上一篇文章再回过头来看这篇就比较容易理解.在本篇中会介绍在独占模式下结点是怎样进入同步队列排队的,以及离开同步队列之前会进行哪些操作.AQS为在独占模

Netty中NioEventLoopGroup的创建源码分析

NioEventLoopGroup的无参构造: 1 public NioEventLoopGroup() { 2 this(0); 3 } 调用了单参的构造: 1 public NioEventLoopGroup(int nThreads) { 2 this(nThreads, (Executor)null); 3 } 继续看到双参构造: 1 public NioEventLoopGroup(int nThreads, Executor executor) { 2 this(nThreads,

关于Java中hashCode方法的实现源码

首先来看一下String中hashCode方法的实现源码. public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } 在String中有一个私有实例字段hash表示该串的哈希值,在

Java线程池ThreadPoolExector的源码分析

前言:线程是我们在学习java过程中非常重要的也是绕不开的一个知识点,它的重要程度可以说是java的核心之一,线程具有不可轻视的作用,对于我们提高程序的运行效率.压榨CPU处理能力.多条线路同时运行等都是强有力的杀手锏工具.线程是如此的重要,那么我们来思考这样一个问题.假设我们有一个高并发,多线程的项目,多条线程在运行的时候,来一个任务我们new一个线程,任务结束了,再把它销毁结束,这样看似没有问题,适合于低并发的场景,可是当我们的项目投入到生产环境,一下涌入千条任务的时候,线程不断的new执行

Java集合系列之LinkedList源码分析

一.LinkedList简介 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的. ps:这里有一个问题,就是关于实现LinkedList的数据结构是否为循环的双向链表,上网搜了有很多文章都说是循环的,并且有的文章中但是我看了源代码觉得应该不是循环的? 例如在删除列表尾部节点的代码: private E unlinkLast(Node<E> l) { final E element = l.item; final Node<E> pr