Java并发编程:Concurrent锁机制解析
Java并发编程:Concurrent锁机制解析
*/-->
code {color: #FF0000}
pre.src {background-color: #002b36; color: #839496;}
Java并发编程:Concurrent锁机制解析
Table of Contents
前面,我们讲了Java自带的对象锁机制。因为我们的方法必然是在一个对象中的,所以,通过对象的锁,可以很好的控制对方法的调用。当对象的锁被一个线程持有后,其他线程想要调用该对象的该方法,就必须进入等待池,等待当前线程执行完毕后,由系统来决定选中谁接下来继续执行。这种方法非常的直观,原理也非常的清晰。
那么,Doug Lea为什么会额外再开发一个并行包呢?
首先,我们从他的Lock锁来看一下,这么做带来的好处。
我觉得最主要的好处是:
- Lock可以查询到更多的信息,包括当前持有的线程,排队等待的线程数量等,这一点很关键,极大的提高了适用范围,这是后面很多的并发类的基础;
- 读写锁的分离,相当于在原有的独占锁的基础上,增加了共享锁。对于不需要同步的方法,使用共享锁,所有线程可以同时调用,仅对外部方法进行同步,这一点可以极大的提高性能。
1 本质
这些锁的本质是一个AbstractQueuedSynchronizer,是一个CLH队列。当锁对象没有被线程持有时(state状态值为0),线程可以获取锁,并将state加1,这时,再有新的线程来获取锁时,就需要放到CLH队列中了,等当前运行的线程将所有的锁释放掉之后,state重新变为0.这时队列中的node可以去获取锁。


2 Lock
Concurrent包中的Lock只是一个接口类,本身并没有实现。它定义了三个主要的方法,lock(),unlock(),newCondition()。lock()用于线程获取锁,执行到该方法时,如果锁没有被线程占有,则把锁分配给线程,如果已经分配,则等待;unlock()用于解除线程锁定;newCondition()用于创建条件。线程获取锁还有三种其他的方式,如是获取之后是否可以被中断,以试探的方式去获取锁等。
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
3 ReentrantLock
ReentrantLock是对Lock接口类的一种实现,本质是一种独占锁。使用一个state来保存一个线程调用lock()的次数。当state为0时,锁可以被线程持有,持有之后将state改为1,这样其他线程就不能再次获得该锁了,只有该线程可以再次持有,这就是重入,也就是这个锁的名字的由来。当该线程调用unlock()时,state值减1,直到state再次等于0,表示该线程完全释放了锁。
这个状态量是用一个int来保存的,并且当值超过int表示的最大正整数,就会溢出变为负数,小于0就会报错。所以,同一个锁最多能重入Integer.MAX_VALUE次,也就是2147483647。
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
至于底层的实现方式,如果看过源代码的话,就会发现基本上是基于CAS实现的,就是compare and swap。就是有一个期望值,当比较当前值与期望值是否相等,当相等时,将值进行更新。所以,当多个线程同时去改变一个值的时候,肯定只有一个线程是可以成功的。因为,这些线程的期望值肯定都是一样的,当其中一个线程修改值之后,其他线程的期望值就对比不成功了。所以,每次最多一个线程能够执行成功。
4 ReadWriteLock
ReadWriteLock同样是一个接口类,有两个方法,分别返回一个读锁和一个写锁,但它们共用一个AQS队列。

Lock readLock();
Lock writeLock();
5 ReentrantReadWriteLock
读写锁的分离意义太重大了,因为很多时候,我们大部分的操作都是在读数据,只有少数情况是需要写数据,如果直接使用同步或者是重入锁,那么性能和效率会非常低。
读锁和写锁有本质的区别,读锁是共享锁,写锁是独占锁。
- 读操作其实是不需要同步的,只有当写操作在进行中时才需要同步等待,所以当没有写操作时,是空锁,所有线程可以同时调用。
- 写操作是必须同步的,所以,一次只有一个线程可以占有写锁。
- 锁升级是不允许的,就是当有读锁在读数据时,写锁是不能被持有的,必须等待所有的读操作完成,再获得写锁。
- 写锁可以降级为读锁,就是写线程可以同时获得读锁,当写锁释放之后,继续持有读锁,然后,再释放读锁。
实际上,读锁和写锁是共用一个AQS队列,状态量state也是共用一个。低16位表示写锁,高16位表示读锁。所以,写锁和读锁的可重入数最多锁65535个。
不同的是,获取锁的方式不同:
// 读锁获取锁的方式,是获取共享锁
public void lock() {
sync.acquireShared(1);
}
// 写锁获取锁的方式,是获取独占锁
public void lock() {
sync.acquire(1);
}
Date: 2017-07-08 10:22
Author: WEN YANG
Created: 2017-07-12 Wed 21:15
Java并发编程:Concurrent锁机制解析的更多相关文章
- Java并发编程之锁机制
锁分类 悲观锁与乐观锁 悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改.因此对于同一个数据的并发操作,悲观锁采取加锁的形式.悲观的认为,不加锁的并发操作一定会出问题 ...
- Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- (转)Java并发编程:volatile关键字解析
转:http://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或 ...
- Java并发编程:volatile关键字解析(转载)
转自https://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 Java并发编程:volatile关键字解析 ...
- Java并发编程:volatile关键字解析-转
Java并发编程:volatile关键字解析 转自海子:https://www.cnblogs.com/dayanjing/p/9954562.html volatile这个关键字可能很多朋友都听说过 ...
- 6、Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- 转:Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字, ...
- [转载]Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- Java并发编程中的设计模式解析(二)一个单例的七种写法
Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...
随机推荐
- ORA-00911: invalid character 错误解决
多数情况如下: 控制面板--系统和安全---系统--高级系统设置--高级--环境变量--系统变量中 变量名:NLS_LANG 变量值:SIMPLIFIED CHINESE_CHINA.ZHS16GBK ...
- SQLite和MySQL数据库的差别与应用
简单来说,SQLITE功能简约.小型化,追求最大磁盘效率:MYSQL功能全面,综合化.追求最大并发效率.假设仅仅是单机上用的,数据量不是非常大.须要方便移植或者须要频繁读/写磁盘文件的话.就用SQLi ...
- CF700E Cool Slogans 后缀自动机 + right集合线段树合并 + 树形DP
题目描述 给出一个长度为n的字符串s[1],由小写字母组成.定义一个字符串序列s[1....k],满足性质:s[i]在s[i-1] (i>=2)中出现至少两次(位置可重叠),问最大的k是多少,使 ...
- JavaScript学习第一篇
在学习之前让我们了解了解JavaScript的由来 Javascript是一种web技术,最初起名叫LiveScript,它是Netscape开发出来一种脚本语言,其目的是为了扩展基本的Html的功能 ...
- .HDF数据库与SQLSERVER / ORACLE的区别
无论ArcGIS的.gbd文件还是MapGIS的.hdf文件,都是数据库文件. 后缀是无意义的.有意义的是其中内在的逻辑和数据结构. https://zhidao.baidu.com/question ...
- pom里引入lib下的包后编译报 package com.sun.crypto.provider does not exist问题解决
最近正在迭代开发的一个项目编译安装时出现报“package com.sun.crypto.provider does not exist”的错误,由于本人能力水平有限,也是第一次遇到该问题,来来回回折 ...
- 622FThe Sum of the k-th Powers
题目大意 求$\sum_{i=1}^{n} i^k$ 分析 我们发现这是一个$k+1$次多项式 因此我们求出前$k+2$项然后插值即可 由于$x_i = i$ 因此公式里面的乘机可以通过预处理然后循环 ...
- NOIP 真题选讲
推荐生要凉辽 这可能是我更新的最后一篇博客 代码什么的有时间再说吧,先讲思路.(已搞定前三题代码) 首先先看一下线段覆盖题.我们有一个区间,要用线段覆盖整个区间. 这个是线段的覆盖简图.我们如何选取最 ...
- 【转】 C语言深度解剖读书笔记(1.关键字的秘密)
本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...
- leetcode 155. 最小栈(c++)
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中.pop() -- 删除栈顶的元素.top() -- 获取栈顶元素.get ...