《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对象实现等待/通知功能的关键。
《Java并发编程的艺术》--Java中的锁的更多相关文章
- 深入介绍Java中的锁[原理、锁优化、CAS、AQS]
1.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 2.锁实现的基本原理 2.1.volatile Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...
- 探究Java中的锁
一.锁的作用和比较 1.Lock接口及其类图 Lock接口:是Java提供的用来控制多个线程访问共享资源的方式. ReentrantLock:Lock的实现类,提供了可重入的加锁语义 ReadWrit ...
- java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁(转载)
之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比>,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高 ...
- Java 中的锁
Java中的锁分类 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分 ...
- Java中的锁(转)
Java中的锁 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂.因为锁(以及其它更高级的线程同步机制)是由synchronized同步 ...
- java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁
之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比>,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高 ...
- 深入理解Java中的锁
转载:https://www.jianshu.com/p/2eb5ad8da4dc Java中的锁 常见的锁有synchronized.volatile.偏向锁.轻量级锁.重量级锁 1.synchro ...
- JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用
Java 中无锁的线程安全整数 AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候, 不可避免的会用到synchron ...
- Java中的锁[原理、锁优化、CAS、AQS]
1.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 2.锁实现的基本原理 2.1.volatile Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...
- 史上最全 Java 中各种锁的介绍
更多精彩原创内容请关注:JavaInterview,欢迎 star,支持鼓励以下作者,万分感谢. 锁的分类介绍 乐观锁与悲观锁 锁的一种宏观分类是乐观锁与悲观锁.乐观锁与悲观锁并不是特定的指哪个锁(J ...
随机推荐
- Java EE之Hibernate异常总结【5】java.lang.StackOverflowError[栈溢出]
Exception in thread "main" java.lang.StackOverflowError at java.lang.reflect.InvocationTar ...
- HNOI2018游记
第一次参加本省省选,结果又是一次划水 Day 0 喝了一个小时鸡汤 大家看看人家钱学森(sheng) 竞赛生要多发展些爱好 不要一考完就fake,那种下考说"大佬AC辣!太强啦!月莫月莫月莫 ...
- CF912E Prime Gift
传送门 看到\(n\)只有16,可以把这些质数分成两半,然后预处理出这些数相乘得出的小于\(10^{18}\)的所有数,排个序,然后二分最终答案,再用两个指针从前往后和从后往前扫,进行\(two-po ...
- oracle建包,函数demo
create table BOOK ( BOOK_ID NUMBER(10), BOOK_NAME VARCHAR2(10) ); create or replace package chen_pac ...
- 【逆向工具】使用x64dbg+spy去除WinRAR5.40(64位)广告弹框
1 学习目标 WinRAR5.40(64位)的弹框广告去除,由于我的系统为x64版本,所以安装了WinRAR(x64)版本. OD无法调试64位的程序,可以让我熟悉x64dbg进行调试的界面. 其次是 ...
- android摄像头(camera)之 v4l2的c测试代码【转】
转自:https://blog.csdn.net/ldswfun/article/details/8745577 在移植android hal的过程中,移植的首要任务是要确保驱动完好,camera是属 ...
- ODOO引用Echarts数据展示
作为一个后端开发,首先想到的是将需要的数据进行处理反馈给前端. 具体如下: 然后就是专门的echarts模块(我这样写主要是因为echarts会用到的地方比较多,后续直接调用) 1. 2.echart ...
- 转载:《理解RESTful架构》 阮一峰
原文:http://www.ruanyifeng.com/blog/2011/09/restful.html 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件&q ...
- 内存分配方式,堆区,栈区,new/delete/malloc/free
1.内存分配方式 内存分配方式有三种: [1]从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. [2]在栈上创建.在执行函数时 ...
- 02-第一个JavaScript代码
在页面中,我们可以在body标签中放入<script type=”text/javascript”></script>标签对儿,<script type=”text/ja ...