• Java并发编程的艺术(八)——锁相关


    锁的作用

    1. 控制多个线程访问共享资源。
    2. 线程协作

    Lock接口

    特点

    1. 与synchronized类似的同步功能,只是需要显式地获取和释放锁。缺少隐式获取锁的便捷性。
    2. 拥有锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种同步特性。
    3. 可扩展性好。
    4. 尝试非阻塞地获取锁:如果当前时刻锁没有被占用,则获得锁。
    5. 能被中断地获取锁:获得锁的线程能被中断,当获得锁的线程被中断,中断异常会被抛出,然后锁会被释放 。
    6. 超时获取锁:在一定时间内尝试获取锁,如果超时,就返回。

    使用方式

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

    注意点

    1. 在finally中释放锁,可以保证在获取锁之后,最终能够得到释放 。
    2. 不要将获取锁放在try中,因为可能发生异常,没有获得锁,最后在finally中导致锁的释放。

    API

    在这里插入图片描述

    队列同步器

    作用

    是用来构建锁或者其他同步组件的基础框架。面向锁的是闲着,简化了锁的实现方式。

    实现原理

    1. 同步队列

    依赖一个FIFO双向队列,当前线程获取同步状态失败的时候,就会把线程及其状态信息做成一个节点入队,并阻塞当前线程。当同步状态释放的时候,就唤醒首节点,进行获取同步状态。

    队列中的节点用来保存获取同步状态失败的线程引用、等待状态以及前驱和后继节点。用过CAS算法设置尾节点。

    当首节点获取同步状态成功时候,就会在释放的时候,唤醒后继节点,然后后继将自己设置为首节点。
    在这里插入图片描述

    2.独占式同步状态获取与释放

    每个节点都在自旋,如果前驱节点是头节点,就尝试获取同步状态;不是就进入等待状态。
    在这里插入图片描述

    3.共享式同步状态获取与释放

    读操作可以是共享,而写操作是独占。

    通过嗲用同步器的acquireShared方法可以共享式地获取同步状态。

    同步队列总结

    1. 是一个获取锁的实现框架,让需要竞争一个锁的线程排队,高效获取。
    2. 如果不能马上获取锁,那么这个线程会进入等待状态,而不是一直去尝试获取,这样降低了开销。

    重入锁

    特点

    1. 支持线程对资源的重复加锁。
    2. 支持获取锁时的公平和非公平性选择。

    实现重进入

    重进入是指线程在获取到锁之后,这个线程再去获取这把锁,不会被这把锁阻塞。

    1. 再次获取:锁需要是被当前线程是否是已经获得自己的线程,如果是,则再次成功获取。
    2. 最终释放:获取了几次,就要释放几次,这样才能让其他线程获取到这把锁。

    公平性与非公平性

    1. 什么是公平性?就是根据线程等待时间的长短,确定哪个线程先获取到锁。
    2. 公平性的问题。非公平的锁开销更小。因为公平性锁采用了FIFO原则将等待线程放进队列,代价是大量的线程切换。

    读写锁

    特点

    1. 不是排他锁,同一时刻可以允许多个读线程访问;
    2. 但是同一时刻只能由一个写线程访问。
    3. 通过读锁和写锁分离,提高并发性。

    实现原理

    1. 写锁是一个支持冲入的排他锁。
    2. 读锁是一个支持重入的共享锁。每次有一个新的线程获取锁,锁状态就增加,这样,如果有写线程想要获取锁就会被阻塞,直到读锁都被释放的时候,写锁才能被获取。
    3. 锁降级。当前线程中的获取的写锁降级为读锁,就是锁降级。过程是:先获取写锁,再获取读锁,最后释放写锁。目的是防止直接放弃写锁之后,其他线程获取到写锁,并修改数据。

    使用方式

    List<Integer> data = new ArrayList<>();
    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    Lock r = rwl.readLock();
    Lock w = rwl.writeLock();
    
    public int get(int idx) {
    	r.lock();
    	try {
    		return data.get(idx);
    	} finally {
    		r.unlock();
    	}
    }
    
    public void add(int num) {
    	w.lock();
    	try {
    		data.add(num);
    	} finally {
    		w.unlock();
    	}
    }
    

    LockSupport工具

    作用

    用于阻塞或唤醒一个线程的公共静态方法。

    API

    在这里插入图片描述

    Condition 接口

    作用

    与Lock配合使用,实现等待和通知模式。

    与对象监视器的区别

    在这里插入图片描述

    使用示例

    Lock lock = new ReentrantLock();
    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();
    	}
    }
    

    API

    在这里插入图片描述

    在这里插入图片描述

    实现原理

    和同步队列类似。

    1.等待队列

    在这里插入图片描述

    等待队列是一个FIFO的队列,每当一个线程调用await()方法,那么这个线程就会释放锁,并进入等待队列。

    2.等待

    线程调用await()方法进入等待态。
    在这里插入图片描述

    3. 通知

    当调用了signal方法,将会唤醒等待队列德首节点。唤醒之前,会将节点移到同步队列中。为什么要放入同步队列中?排队去获取锁啊。
    在这里插入图片描述

    等待队列总结

    condition的等待队列像是同步队列下的一个缓冲,同步队列是竞争锁的队列,只有首节点获取到了锁,如果这个节点的线程调用了await方法,那么那就从同步队列的首节点跑到了等待队列了尾节点。其他线程调用了signal方法,那么等待队列的首就跑到同步队列的尾部,进行排队竞争锁。

  • 相关阅读:
    数据库异常处理记录
    FINEMVC重定向和显示合计
    有意思的文章的链接
    oralce 创建用户和权限
    FINEUI(MVC) grid 双击弹窗功能
    FINEUI(MVC)布局问题记录
    通过判断cookie过期方式向Memcached中添加,取出数据(Java)
    通过数组方式向Oracle大批量插入数据(10万条11秒)
    Python基础学习13--面向对象
    Python基础学习12--变量作用域
  • 原文地址:https://www.cnblogs.com/lippon/p/14117662.html
Copyright © 2020-2023  润新知