锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到目标的内容!对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 锁原理的更多相关文章

  1. JAVA CAS原理深度分析 volatile,偏向锁,轻量级锁

    JAVA CAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651 偏向锁,轻量级锁 https://blog.csdn.net/zqz ...

  2. (转载)java高并发:CAS无锁原理及广泛应用

    java高并发:CAS无锁原理及广泛应用   版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...

  3. Java进阶专题(二十五) 分布式锁原理与实现

    前言 ​ 现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计.那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用.那么既然产生了服务调用,就必然会存在服务调用延迟或失败 ...

  4. java 锁!

    问题:如何实现死锁. 关键: 1 两个线程ta.tb 2 两个对象a.b 3 ta拥有a的锁,同时在这个锁定的过程中,需要b的锁:tb拥有b的锁,同时在这个锁定的过程中,需要a的锁: 关键的实现难点是 ...

  5. Java编译原理

    http://wenku.baidu.com/view/f9b1734b87c24028915fc3a3.html Java编译原理 1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想 ...

  6. JAVA 锁之 Synchronied

    ■ Java 锁 1. 锁的内存语义 锁可以让临界区互斥执行,还可以让释放锁的线程向同一个锁的线程发送消息 锁的释放要遵循 Happens-before 原则(锁规则:解锁必然发生在随后的加锁之前) ...

  7. 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

    首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...

  8. zookeeper 分布式锁原理

    zookeeper 分布式锁原理: 1 大家也许都很熟悉了多个线程或者多个进程间的共享锁的实现方式了,但是在分布式场景中我们会面临多个Server之间的锁的问题,实现的复杂度比较高.利用基于googl ...

  9. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

随机推荐

  1. Tomcat性能优化及常用命令整理

    1汤姆猫性能优化 1.1连接参数 1.1.1默认连接配置 默认连接器采用阻塞式 IO,默认最大线程数为200,配置如下: <Connector port="8080" pro ...

  2. UNIX网络编程——客户/服务器心搏函数

    阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...

  3. 关于大数据时代传统商业存储的思考: 中心存储 VS 分布式存储

    尊重原创,转载请注明出处:http://anzhan.me ; http://blog.csdn.net/anzhsoft 今天和我们部门的老大1*1, 大家面对面沟通了一下到新的项目组的想法.而且也 ...

  4. Win7 Eclipse Hadoop2.4插件配置

    准备工作: 1.下载hadoop2x-eclipse-plugin-master.zip Github地址:https://github.com/winghc/hadoop2x-eclipse-plu ...

  5. 关于APP界面布局设计的八种优缺点

    学习UI设计的时候,经常要接触到页面的布局,布局的方式会直接影响一个APP的视觉效果,好的布局方式,往往能带来舒服的视觉效果,更能得到用户的接受与好评.然而万变不离其宗,移动端页面常用的布局,不外乎以 ...

  6. Hibernate查询之SQL查询,查询结果用new新对象的方式接受,hql查询,通过SQL查询的结果返回到一个实体中,查询不同表中内容,并将查到的不同表中的内容放到List中

     package com.ucap.netcheck.dao.impl; import java.util.ArrayList;import java.util.List; import org. ...

  7. python 内存NoSQL数据库

    python 内存NoSQL数据库 来自于网络,经过修改,秉承Open Source精神,回馈网络! #!/usr/bin/python #-*- coding: UTF-8 -*- # # memd ...

  8. HTTP 消息结构

    HTTP 消息结构 HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议. 一个HTTP"客户端"是一个应用程序(Web浏览 ...

  9. 利用Dijkstra算法实现记录每个结点的所有最短路径

    最近在做PAT时发现图论的一些题目需要对多条最短路径进行筛选,一个直接的解决办法是在发现最短路径的时候就进行判断,选出是否更换路径:另一个通用的方法是先把所有的最短路径记录下来,然后逐个判断.前者具有 ...

  10. numpy教程:矩阵matrix及其运算

    http://blog.csdn.net/pipisorry/article/details/48791403 numpy矩阵简介 NumPy函数库中存在两种不同的数据类型(矩阵matrix和数组ar ...