线程(六)之LOCK和synchronized
在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantReadWriteLock),其实现都依赖java.util.concurrent.AbstractQueuedSynchronizer类,实现思路都大同小异,因此我们以ReentrantLock作为讲解切入点。
ReentrantLock的调用过程
ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer:
abstract static class Sync extends AbstractQueuedSynchronizer
Sync又有两个子类:
final static class NonfairSync extends Sync
final static class FairSync extends Sync
显然是为了支持公平锁和非公平锁而定义,默认情况下为非公平锁。
先理一下Reentrant.lock()方法的调用过程(默认非公平锁):
锁实现(加锁)
简单说来,AbstractQueuedSynchronizer会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全部处于阻塞状态,经过调查线程的显式阻塞是通过调用LockSupport.park()完成,而LockSupport.park()则调用sun.misc.Unsafe.park()本地方法,再进一步,HotSpot在Linux中中通过调用pthread_mutex_lock函数把线程交给系统内核进行阻塞。
CLH锁即Craig, Landin, and Hagersten (CLH) locks,CLH锁是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性。
CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
与synchronized相同的是,这也是一个虚拟队列,不存在队列实例,仅存在节点之间的前后关系。令人疑惑的是为什么采用CLH队列呢?原生的CLH队列是用于自旋锁,但Doug Lea把其改造为阻塞锁。
当有线程竞争锁时,该线程会首先尝试获得锁,这对于那些已经在队列中排队的线程来说显得不公平,这也是非公平锁的由来,与synchronized实现类似,这样会极大提高吞吐量。
如果已经存在Running线程,则新的竞争线程会被追加到队尾,具体是采用基于CAS的Lock-Free算法,因为线程并发对Tail调用CAS可能会导致其他线程CAS失败,解决办法是循环CAS直至成功。
Sync.nonfairTryAcquire
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) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
该方法会首先判断当前状态,如果c==0说明没有线程正在竞争该锁,如果不c !=0 说明有线程正拥有了该锁。
如果发现c==0,则通过CAS设置该状态值为acquires,acquires的初始调用值为1,每次线程重入该锁都会+1,每次unlock都会-1,但为0时释放锁。如果CAS设置成功,则可以预计其他任何线程调用CAS都不会再成功,也就认为当前线程得到了该锁,也作为Running线程,很显然这个Running线程并未进入等待队列。
如果c !=0 但发现自己已经拥有锁,只是简单地++acquires,并修改status值,但因为没有竞争,所以通过setStatus修改,而非CAS
公平锁和非公平锁
tryAcquire
是一个抽象方法,是公平与非公平的实现原理所在。addWaiter
是将当前线程结点加入等待队列之中。公平锁在锁释放后会严格按照等到队列去取后续值,而非公平锁在对于新晋线程有很大优势。acquireQueued
在多次循环中尝试获取到锁或者将当前线程阻塞。selfInterrupt
如果线程在阻塞期间发生了中断,调用Thread.currentThread().interrupt()
中断当前线程。
公平锁和非公平锁在说的获取上都使用到了 volatile
关键字修饰的state
字段, 这是保证多线程环境下锁的获取与否的核心。
但是当并发情况下多个线程都读取到 state == 0
时,则必须用到CAS技术,一门CPU的原子锁技术,可通过CPU对共享变量加锁的形式,实现数据变更的原子操作。
volatile 和 CAS的结合是并发抢占的关键。
非公平锁:非公平锁在实现的时候多次强调随机抢占,与公平锁的区别在于新晋获取锁的进程会有多次机会去抢占锁。如果被加入了等待队列后则跟公平锁没有区别。
static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;/*** Performs lock. Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1); } //
tryAcquire
是一个抽象方法,是公平与非公平的实现原理所在。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);}}
公平锁:公平锁的实现机理在于每次有线程来抢占锁的时候,都会检查一遍有没有等待队列,如果有, 当前线程会执行如下步骤:
其中hasQueuedPredecessors
是用于检查是否有等待队列的。
if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;
}
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //其中hasQueuedPredecessors
是用于检查是否有等待队列的。 if (!hasQueuedPredecessors() && 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; } }
Lock和Synchronized 区别 https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1
1)synchronized是Java语言的关键字,因此是内置特性,Lock不是Java语言内置的,Lock是一个接口,通过实现类可以实现同步访问。
2)synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
3)在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 是一个类 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
线程的状态
BLOCKED状态
线程处于BLOCKED状态的场景。
- 当前线程在等待一个monitor lock,比如等待执行synchronized代码块或者使用synchronized标记的方法。
- 在synchronized块中循环调用Object类型的wait方法,如下是样例
synchronized(this)
{
while (flag)
{
obj.wait();
}
// some other code
}
WAITING状态
线程处于WAITING状态的场景。
- 调用Object对象的wait方法,但没有指定超时值。
- 调用Thread对象的join方法,但没有指定超时值。
- 调用LockSupport对象的park方法。
提到WAITING状态,顺便提一下TIMED_WAITING状态的场景。
TIMED_WAITING状态
线程处于TIMED_WAITING状态的场景。
- 调用Thread.sleep方法。
- 调用Object对象的wait方法,指定超时值。
- 调用Thread对象的join方法,指定超时值。
- 调用LockSupport对象的parkNanos方法。
- 调用LockSupport对象的parkUntil方法。
https://blog.csdn.net/bingjing12345/article/details/17789613
https://blog.csdn.net/Luxia_24/article/details/52403033
转载于:https://www.cnblogs.com/fanBlog/p/9634553.html
线程(六)之LOCK和synchronized相关推荐
- 【并发】线程同步——锁Lock与synchronized比较
一.简单使用Lock锁 Java 5中引入了新的锁机制--java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lo ...
- 【java线程】锁机制:synchronized、Lock、Condition
[Java线程]锁机制:synchronized.Lock.Condition 原创 2013年08月14日 17:15:55 标签:Java /多线程 74967 http://www.infoq. ...
- Lock和synchronized比较详解
原文:http://www.cnblogs.com/handsomeye/p/5999362.html 今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉 ...
- Lock与synchronized 的区别
1.ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候 线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O ...
- Java并发编程:Lock和Synchronized 转
在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...
- Lock接口Condition,以及Lock与synchronized异同
一.synchronized与Lock 基于synchronized关键字去实现同步的,(jvm内置锁,也叫隐式锁,由我们的jvm自动去加锁跟解锁的)juc下的基于Lock接口的这样的一种锁的实现方式 ...
- Lock和synchronized比较详解(转)
从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...
- Lock和synchronized的选择
学习资源:http://www.cnblogs.com/dolphin0520/p/3923167.html 一.java.util.concurrent.locks包下常用的类 1.Lock pub ...
- 线程高级篇-Lock锁和Condition条件
浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其 ...
最新文章
- Java虚拟机:对象创建过程与类加载机制、双亲委派模型
- 测试手机信号格数软件,超详细教程之教你如何查询手机信号的强度
- Altium 原理图出现元件“Extra Pin…in Normal of part ”警告的解决方法
- [html] From表单提交时为什么会刷新页面?怎么预防刷新?
- html pdf文档的格式控制符,关于Unicode控制字符RLO,LRO,PDF 的坑
- poj2280--Amphiphilic Carbon Molecules(扫描线+极角排序+转换坐标)
- iOS 如何把图片资源打包成bundle文件及遇到的坑(详解)
- [看过不再忘]详细解析chmod修改文件权限
- 动态规划解最长公共子串
- python爬取小猪短租信息
- rest assured
- NetBeans ide操作流程及注意事项
- evolution ubuntu邮箱_Ubuntu evolution 邮件客户端配置详解(图)
- Qt ApplicationAttribute/WidgetAttribute 程序级别属性
- Js实现购物车加减,价格计算等功能
- opencv生成3d模型_OpenCV4.2使用viz模块显示3D图像
- Procmon 的使用
- 计算机函数的输入,向计算机输入复杂的数学函数公式怎样处理?
- android点击按钮发出声音
- 树莓派控制继电器开关工作