博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
同步锁——ReentrantLock
阅读量:4095 次
发布时间:2019-05-25

本文共 4163 字,大约阅读时间需要 13 分钟。


本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。

并发编程系列博客传送门


Lock接口简介

热血小说 www.v1122.com

在JUC包下面有一个java.util.concurrent.locks包,这个包提供了一系列基础的锁工具,对传统的synchronizd、wait和notify等同步机制进行补充和增强。下面先来介绍下这个Lock接口。

Lock接口可以视为synchronized的增强版,提供了更灵活的功能。相对于synchronized,Lock接口还提供了限时锁等待、锁中断和锁尝试等功能。该接口的定义如下

public interface Lock {// 尝试去获得锁// 如果锁不可用,当前线程会变得不可用,直到获得锁为止。(中途会忽略中断)    void lock();// 尝试去获取锁,如果锁获取不到,线程将不可用// 知道获取锁,或者被其他线程中断// 线程在获取锁操作中,被其他线程中断,则会抛出InterruptedException异常,并且将中断标识清除。void lockInterruptibly() throws InterruptedException;// 锁空闲时返回true,锁不空闲是返回falseboolean tryLock();// 在unit时间内成功获取锁,返回true// 在unit时间内未成功获取锁,返回false// 如果当前线程在获取锁操作中,被其他线程中断,则会抛出InterruptedException异常,并且将中断标识清除。boolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 释放锁void unlock();// 获取一个绑定到当前Lock对象的Condition对象// 获取Condition对象的前提是当前线程持有Lock对象Condition newCondition();}

关于上面的lock()和lockInterruptibly()方法,有如下区别:

lock()方法类似于使用synchronized关键字加锁,如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。

lockInterruptibly()方法顾名思义,就是如果锁不可用,那么当前正在等待的线程是可以被中断的,这比synchronized关键字更加灵活。

Lock接口的经典用法

Lock lock = new ReentrantLock();if (lock.tryLock()) {    try {        // manipulate protected state    } finally {        lock.unlock();    }} else {    // perform alternative actions}

ReentrantLock

ReentrantLock类是一个可重入的独占锁,除了具有和synchronized一样的功能外,还具有限时锁等待、锁中断和锁尝试等功能。

ReentrantLock底层是通过继承AQS来实现独占锁功能的。

公平锁和非公平锁

关于ReentrantLock,有两个很重要的概念需要学习:公平锁和非公平锁

查看ReentrantLock的源代码,我们会看到两个构造函数,分为对应构造公平锁和非公平锁。

//默认构造非公平锁public ReentrantLock() {    sync = new NonfairSync();}//true构造公平锁,false构造非公平锁public ReentrantLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();}

公平锁:是指线程在抢占锁失败后会进入一个等待队列,先进入队列的线程会先获得锁。公平性体现在先来先得。

非公平锁:是指线程抢占锁失败后会进入一个等待队列,但是这些等待线程谁能先获得锁不是按照先来先得的规则,而是随机的。不公平性体现在后来的线程可能先得到锁。

如果有很多线程竞争一把公平锁,系统的总体吞吐量(即速度很慢,常常极其慢)比较低,因为此时在线程调度上面的开销比较大。

原因是采用公平策略时,当一个线程释放锁时,需要先将等待队列中的线程唤醒。这个唤醒的调度过程是比较耗费时间的。如果使用非公平锁的话,当一个线程释放锁之后,可用的线程能立马获得锁,效率较高。

ReentrantLock代码实现

1. 非公平锁代码

static final class NonfairSync extends Sync {    private static final long serialVersionUID = 7316153563782823691L;    /**     * Performs lock.  Try immediate barge, backing up to normal     * acquire on failure.     */    final void lock() {        //如果没有线程占据锁,则占据锁,也就是将state从0设置为1        //这种抢占方式不要排队,有人释放了锁,你可以直接插到第一位        //去抢,只要你能抢到        if (compareAndSetState(0, 1))            setExclusiveOwnerThread(Thread.currentThread());        else        //否则尝试抢占锁            acquire(1);    }    protected final boolean tryAcquire(int acquires) {        return nonfairTryAcquire(acquires);    }}

通过之前对AQS的介绍,我们知道抢占锁的时候会调用 tryAcquire 方法。非公平锁的这个方法直接调用了父类中的nonfairTryAcquire

final boolean nonfairTryAcquire(int acquires) {    final Thread current = Thread.currentThread();    int c = getState();    //锁已经被释放,则直接占据锁    if (c == 0) {        if (compareAndSetState(0, acquires)) {            setExclusiveOwnerThread(current);            return true;        }    }    //否则判断锁是不是之前被自己占用过,并设置重入次数    else if (current == getExclusiveOwnerThread()) {        int nextc = c + acquires;        if (nextc < 0) // overflow            throw new Error("Maximum lock count exceeded");        setState(nextc);        return true;    }    return false;}

1. 公平锁代码

static final class FairSync extends Sync {        private static final long serialVersionUID = -3000897897090466540L;        final void lock() {            acquire(1);        }                protected final boolean tryAcquire(int acquires) {            final Thread current = Thread.currentThread();            int c = getState();            if (c == 0) {                //没人在队列中排队,并且锁已经被释放才能抢占到锁,否则去队列中排队                if (!hasQueuedPredecessors() &&                    compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            //设置重入次数            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0)                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }    }

转载地址:http://abaii.baihongyu.com/

你可能感兴趣的文章
inet_ntoa、 inet_aton、inet_addr
查看>>
用模板写单链表
查看>>
用模板写单链表
查看>>
链表各类操作详解
查看>>
C++实现 简单 单链表
查看>>
数据结构之单链表——C++模板类实现
查看>>
Linux的SOCKET编程 简单演示
查看>>
正则匹配函数
查看>>
Linux并发服务器编程之多线程并发服务器
查看>>
聊聊gcc参数中的-I, -L和-l
查看>>
[C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)
查看>>
C语言内存检测
查看>>
Linux epoll模型
查看>>
Linux select TCP并发服务器与客户端编程
查看>>
Linux系统编程——线程池
查看>>
基于Visual C++2013拆解世界五百强面试题--题5-自己实现strstr
查看>>
Linux 线程信号量同步
查看>>
C++静态成员函数访问非静态成员的几种方法
查看>>
类中的静态成员函数访问非静态成员变量
查看>>
C++学习之普通函数指针与成员函数指针
查看>>