有关Java 锁原理
锁
锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到目标的内容!对Java并发包,锁的实现基本在java.util.concurrent.locks包中,说“基本”是因为,在java.util.concurrent中还有CountDownLatch(可以看成带计数器的锁),CyclicBarrier,Semaphore(类似于信号量,但是也可以看成一个特殊的锁)。这里我们先以java.util.concurrent.locks包为主来讨论锁的问题吧。我们来看看java.util.concurrent.locks包中到底有多少“锁”,主要是这样几个接口及其实现:
Lock----------
|---------ReadLock //读锁
|------ReentrantLock //并发锁
|------WriteLock //写锁
ReadWriteLock----------------
|-------------ReentrantReadWriteLock //并发读写锁
以上:Lock和ReadWriteLock都是接口,是锁行为的契约,其余部分都是实现,而ReadLock和WriteLock是作为ReentrantReadWriteLock的静态内部类存在,不能直接使用,也就是他们是ReentrantReadWriteLock的支持类,不是向外提供给外部程序使用的!
另外还发现有这样抽象类:
AbstractOwnableSynchronizer---------
|----------AbstractQueuedLongSynchronizer
|-------AbstractQueuedSynchronizer
这几个都是抽象类,但是没有明显的实现!没有实现?其实是所有的Lock在其实现时使用的基础结构,并且以来它们实现了锁的机制,只要我们先把这几个抽象类的实现逻辑搞清楚,Lock的原理就比较清楚了,理解了原理,使用Lock时就有的放矢了。
另外包中还有condition接口:
Condition------------
|--------- ConditionObject
这个接口和实现是干什么的?是用来实现“竞赛条件”的,提供更加细粒度的线程锁控制,也就是在某个条件下加锁等操作。
好了,java.util.concurrent.locks 中的基本“布局”和“结构”现在已经比较清楚了,下面我们将从AbstractOwnableSynchronizer抽象类开始讲,这是整个Lock的核心,明白其实现能让你了解如何“准确”的使用锁,而不是仅仅“知道电视机的按钮而不知道里面的原理”,毕竟我们是搞开发的,对原理更应该重视,你说是吧?
AbstractOwnableSynchronizer:
它依靠于CLH队列来实现锁的机制
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
采用的CHL模型采用下面的算法完成FIFO的入队列和出队列过程,而正是入队和出队的实现“模拟”了“锁”的效用!
对于入队列(enqueue):采用CAS操作,每次比较尾结点是否一致,然后插入的到尾结点中。
do {
pred = tail;
}while ( !compareAndSet(pred,tail,node) );
对于出队列(dequeue):由于每一个节点也缓存了一个状态,决定是否出队列,因此当不满足条件时就需要自旋等待,一旦满足条件就将头结点设置为下一个节点。
while (pred.status != RELEASED) ;
head = node;
有三个核心字段:
private volatile int state;
private transient volatile Node head;
private transient volatile Node tail;
其中state描述的有多少个线程取得了锁,对于互斥锁来说state<=1。head/tail加上CAS()操作就构成了一个CHL的FIFO队列。下面是Node节点的属性。
volatile int waitStatus; 节点的等待状态,一个节点可能位于以下几种状态:
CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。
SIGNAL = -1: 节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。
CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。
0: 正常状态,新生的非CONDITION节点都是此状态。
非负值标识节点不需要被通知(唤醒)。
volatile Node prev;此节点的前一个节点。节点的waitStatus依赖于前一个节点的状态。
volatile Node next;此节点的后一个节点。后一个节点是否被唤醒(uppark())依赖于当前节点是否被释放。
volatile Thread thread;节点绑定的线程。
Node nextWaiter;下一个等待条件(Condition)的节点,由于Condition是独占模式,因此这里有一个简单的队列来描述Condition上的线程节点。
以上只是一个简单的描述吧,只要知道AbstractOwnableSynchronizer的核心是一个CHL队列,而AbstractOwnableSynchronizer是所有Lock的基础,在讲具体Lock的时候,我将“回溯”到AbstractOwnableSynchronizer,凡是具体Lock调用AbstractOwnableSynchronizer的方法的时候,我将具体讲一下其实现,并对应着Lock对应方法的实现,这样就能彻底搞清楚具体Lock的原理了,也就能在使用时游刃有余了。
对于那些真正需要探知Lock一切底细的家伙而言,我找了个文档,作为附件,是并发作者的论文,不过你需要英文好一些才可以哦
有关Java 锁原理的更多相关文章
- JAVA CAS原理深度分析 volatile,偏向锁,轻量级锁
JAVA CAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651 偏向锁,轻量级锁 https://blog.csdn.net/zqz ...
- (转载)java高并发:CAS无锁原理及广泛应用
java高并发:CAS无锁原理及广泛应用 版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...
- Java进阶专题(二十五) 分布式锁原理与实现
前言 现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计.那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用.那么既然产生了服务调用,就必然会存在服务调用延迟或失败 ...
- java 锁!
问题:如何实现死锁. 关键: 1 两个线程ta.tb 2 两个对象a.b 3 ta拥有a的锁,同时在这个锁定的过程中,需要b的锁:tb拥有b的锁,同时在这个锁定的过程中,需要a的锁: 关键的实现难点是 ...
- Java编译原理
http://wenku.baidu.com/view/f9b1734b87c24028915fc3a3.html Java编译原理 1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想 ...
- JAVA 锁之 Synchronied
■ Java 锁 1. 锁的内存语义 锁可以让临界区互斥执行,还可以让释放锁的线程向同一个锁的线程发送消息 锁的释放要遵循 Happens-before 原则(锁规则:解锁必然发生在随后的加锁之前) ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
- zookeeper 分布式锁原理
zookeeper 分布式锁原理: 1 大家也许都很熟悉了多个线程或者多个进程间的共享锁的实现方式了,但是在分布式场景中我们会面临多个Server之间的锁的问题,实现的复杂度比较高.利用基于googl ...
- 转 : 深入解析Java锁机制
深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...
随机推荐
- ASP.net 路径问题 详解
各位有没有碰到在日常工作中经常在路径设置的时候把 "~/ ../ .../ . / .http://www.cnblogs.com/"这些符号搞混搞乱了?偶尔还会因路径的问题郁闷了 ...
- UNIX网络编程——SOCKET API和TCP STATE的对应关系_三次握手_四次挥手及TCP延迟确认
在socket系统调用中,如何完成三次握手和四次挥手: SOCK_DGRAM即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立连接的过程,即没有发包,close也不 ...
- CMake设置FOLDER失败及解决
CMake可以设置FOLDER属性,用来分目录组织VC中的多个工程. FOLDER: Set the folder name. Use to organize targets in an IDE. T ...
- Android开发技巧——PagerAdapter实现类的封装
ViewPager是android的support库中的一个控件,也是一个在许多应用开发中都用得比较多的控件.目前为止,对ViewPager的使用,我一般是用ViewPager + Fragment的 ...
- Hibernate与Spring的事务管理
什么是事务 这个问题比较大,按照我的理解就是,一个事务内的n个操作,要么全部完成,一旦有一个操作有问题,那么所有的操作都全部回滚. Jdbc的事务 首先,大家已经知道了,事务说白了就是一个词----统 ...
- 【嵌入式开发】gcc 学习笔记(一) - 编译C程序 及 编译过程
一. C程序编译过程 编译过程简介 : C语言的源文件 编译成 可执行文件需要四个步骤, 预处理 (Preprocessing) 扩展宏, 编译 (compilation) 得到汇编语言, 汇编 (a ...
- 【Unity Shaders】Diffuse Shading——在Surface Shader中使用properties
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 《java入门第一季》之面向对象(内部类到底在哪里?)
/* 内部类概述: 把类定义在其他类的内部,这个类就被称为内部类. 举例:在类A中定义了一个类B,类B就是内部类. 内部的访问特点: A:内部类可以直接访问外部类的成员,包括私有. B:外部类要访问内 ...
- 给Cocos2D视图添加手势支持
见如下代码: UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self ac ...
- Android For JNI(二)——C语言中的数据类型,输出,输入函数以及操作内存地址,内存修改器
Android For JNI(二)--C语言中的数据类型,输出,输入函数以及操作内存地址,内存修改器 当我们把Hello World写完之后,我们就可以迈入C的大门了,今天就来讲讲基本的一些数据类型 ...