当前位置: 首页 > news >正文

《Java并发编程的艺术》--Java中的锁

No1:

Lock接口

Lock lock = new ReentrantLock();
lock.lock();
try{
}finally{
    lock.unlock();
}

No2:

不要讲获取锁的过程写在try块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放

No3:

No4:

队列同步器(同步器)是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,并发包的作者期望它能够成为实现大部分同步需求的基础。

主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。

No5:

锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节

同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。

锁和同步器很好地隔离了使用者和实现者所需关注的领域。

No6:

同步器提供的模板方法基本上分为3类:

1)独占式获取与释放同步状态

2)共享式获取与释放同步状态

3)查询同步队列中的等待线程情况

No7:

独占锁:在同一时刻只能有一个线程获取到锁,而其他获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后继的线程才能够获取锁。

class Mutex implements Lock{
    //静态内部类,自定义同步器
    private static class Sync extends AbstractQueueSynchronizer{
        //是否处于占用状态
        protected boolean isHeldExclusively(){
            return getState() == 1;
        }
        //当状态为0的时候获取锁
        public boolean tryAcquire(int acquires){
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        //释放锁,将状态设置为0
        protected boolean tryRelease(int release){
            if(getState() == 0)throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        //返回一个Condition,每个condition都包含了一个condition队列
        Condition newCondition(){
            return new Condition();
        }
    }
    //仅需要将操作代理到Sync上即可
    private final Sync sync = new Sync();
    public void lock(){sync.acquire(1)};
    public boolean tryLock(){
        return sync.tryAcquire(1);
    }
    public void unlock(){
        sync.release(1);
    }
    public Condition newCondition(){
        return sync.newCondition();
    }
    public boolean isLocked(){
        return sync.isHeldExclusively();
    }
    public boolean hasQueuedTreads(){
        return sync.hasQueuedTreads();
    }
    public void lockInterruptibly() throws InterruptedException{
        sync.acquireInterruptibly(1);
    }
    public boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException{
        return sync.tryAcquireNanos(1,unit.toNanos(timeout));
    }
}

No8:

重入锁ReentrantLock,就是支持重进入得锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性选择

No9:

重进入时指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞,该特性的实现需要解决以下两个问题

1)线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取

2)锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经成功释放

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){
            throw new Error("Maximum lock count exceeded");
        }
        setState(nextc);
        return true;
    }
    return false;
}

protected final boolean tryRelease(int release){
    int c = getState() - release;
    if(Thread.currentThread()!=getExclusiveOwnerThread()){
        throw new IllegalMonitorStateException();
    }
    boolean free = false;
    if(c == 0){
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

 No10:

独占锁和重入锁都是排他锁,这些锁在同一时刻只允许一个线程进行访问。

而读写锁ReentrantReadWriteLock在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他线程均被阻塞。

读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升

public class Cache{
    static Map<String,Object> map = new HashMap<String,Object>();
    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    static Lock r = rwl.readLock();
    static Lock w = rwl.writeLock();
    //获取一个key对应的value
    public static final Object get(String key){
        r.lock();
        tyr{
            return map.get(key);
        }finally{
            r.unlock();
        }
    }
    //设置key对应的value,并返回旧的value
    public static final Object put(String key,Object value){
        w.lock();
        try{
            return map.put(key,value);
        }finally{
            w.unlock();
        }
    }
    //清空所有的内容
    public static final void clear(){
        w.lock();
        try{
            map.clear();
        }finally{
            w.unlock();
        }
    }
}

 当获取写锁后,其他线程对于读锁和写锁的获取均被阻塞,而只有写锁被释放之后,其他读写操作才能继续。

No11:

读锁是一个支持重进入的共享锁,它能够被多个线程同时获取,在没有其他写线程访问时,读锁总会被成功的获取,而所做的也只是(线程安全的)增加读状态。

 No12:

锁降级是指写锁降级成为读锁。是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

public void processData(){
    readLock.lock();
    if(!update){
        //必须先释放读锁
        readLock.unlock();
        //锁降级从写锁获取到开始
        writeLock.lock();
        try{
            if(!update){
                //准备数据的流程(略)
                update = true;
            }
            readLock.lock();
        }finally{
            writeLock.unlock();
        }
        //锁降级完成,写锁降级为读锁
    }
    try{
        //使用数据的流程(略)
    }finally{
        readLock.unlock();
    }
}

No12:

RentrantReadWriteLock不支持锁升级(把持读锁、获取写锁,最后释放读锁的过程)。目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取到读锁的线程是不可见的。

No13:

No14:

Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,COndition是依赖Lock对象的

Lock lock = new ReentranLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException{
    lock.lock();
    try{
        condition.await();
    }finally{
        lock.unlock();
    }
}
public void conditionSignal() throws InterruptedException{
    lock.lock();
    try{
        condition.signal();
    }finally{
        lock.unlock();
    }
}

 

No15:

ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队列,该队列是Condition对象实现等待/通知功能的关键。

转载于:https://www.cnblogs.com/anni-qianqian/p/8531096.html

相关文章:

  • win10 vs2015源码编译tesseract4.0
  • Go语言备忘录(2):反射的原理与使用详解
  • ubuntu16.04 更换源
  • Django中间件middleware
  • 结构图
  • libimobiledevice --Mingw32交叉编译
  • 在c:forEach作用域外使用标签所产生的值
  • 04-手机套餐:建造者模式
  • css总结1:position定位:absolute/relative/fixed
  • zzw原创_非root用户启动apache的问题解决(非root用户启动apache的1024以下端口)
  • SQL循环语句 详解
  • OpenCV问题集锦
  • 20154327 Exp1 PC平台逆向破解
  • ios 通知与通知传值2018.03.17
  • 20155307《网络对抗》PC平台逆向破解(二)
  • conda常用的命令
  • HashMap剖析之内部结构
  • JavaScript 奇技淫巧
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • leetcode-27. Remove Element
  • ng6--错误信息小结(持续更新)
  • Shadow DOM 内部构造及如何构建独立组件
  • vue.js框架原理浅析
  • 产品三维模型在线预览
  • 浮动相关
  • 技术发展面试
  • 面试总结JavaScript篇
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 深入浅出Node.js
  • 手写双向链表LinkedList的几个常用功能
  • 推荐一个React的管理后台框架
  • 微信支付JSAPI,实测!终极方案
  • 小试R空间处理新库sf
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • ​虚拟化系列介绍(十)
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • #前后端分离# 头条发布系统
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (一)基于IDEA的JAVA基础12
  • (转)ABI是什么
  • (转)母版页和相对路径
  • (转载)hibernate缓存
  • (转载)利用webkit抓取动态网页和链接
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .gitignore文件—git忽略文件
  • .Net core 6.0 升8.0
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数