unit类型是什么?_项目中有用过锁吗?能解释一下什么是AQS(AbstractQueuedSynchronizer)吗?...

9ac9150168564ad07950775ab9740d2e.png

b6d7039a930cb64de2ddf5ba7bc6c3bf.png

1 前言

锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,如读写锁)。在以前,Java程序是靠synchronized来实现锁功能的,而在Java SE 5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能,他提供了与synchronized关键字类似的同步功能,只是在使用时需要显式的获取锁和释放锁,虽然它缺少了synchronized提供的隐式获取释放锁的便捷性,但是却拥有了锁获取和释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字不具备的同步特性。很多锁都通过实现Lock接口来完成对锁的操作,比如可重入锁(ReentrantLock)、前一张讲的Redisson分布式锁等,而Lock接口的实现,基本是都是通过聚合了一个同步器的子类来完成线程访问控制的,而同步器,就是我们常说的AQS(AbstractQueuedSynchronizer),也是今天要记录的内容。

2 什么是AQS

AQS(队列同步器AbstractQueuedSynchronizer)是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。

0e8899920bc43db92fce1797c15bd927.png

如上图所示,同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态进行修改,这时就需要使用同步器提供的3个方法来进行操作:
1、getState():获取当前同步状态

fdb09b3f3f0fda172fb1c8eb851ea2b0.png

2、setState():设置当前同步状态

d60fc4649b9fa193f9538035340c08c7.png

3、compareAndSetState(int expect, int update):通过CAS设置当前状态,该方法能保证状态设置的原子性

077758da33f1cb9b8b0c7544a4e499c9.png

子类推荐被定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用。同步器既可以支持独占式获取同步状态(单个线程获取锁),也可以支持共享式获取同步状态(多个线程获取到锁),这样就可以方便实现不同类型的同步组件(如上图所示的可重入锁:ReentrantLock、可重入读写锁:ReentrantReadWriteLock、计数器:CountDownLatch等等)

3 同步器可重写的方法

以下代码为可重入锁继承同步器后重写的方法:

  • protected boolean tryAcquire(int acquires):独占式获取同步锁状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行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);}protected final boolean tryAcquire(int acquires) {// nonfairTryAcquire方法和下面的公平锁方法除了判断是否在队列首位之外没有不同return nonfairTryAcquire(acquires);}}/*** 公平锁*/static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}/*** Fair version of tryAcquire.  Don't grant access unless* recursive call or no waiters or is first.*/protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();// 获取当前同步状态int c = getState();// 判断是否符合预期if (c == 0) {// 判断是否在队列首位并且CAS设置当前状态成功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;}}
  • protected boolean tryRelease(int acquires):独占式释放同步锁状态,等待获取同步状态的线程将有机会获取同步状态。
protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}
  • protected boolean isHeldExclusively():当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程锁独占。
protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}

以下代码为读写锁继承同步器后重写的方法:

  • protected int tryAcquireShared(int arg):共享式获取同步状态,返回大于0的值,表示获取锁成功,反之获取锁失败。
protected final int tryAcquireShared(int unused) {Thread current = Thread.currentThread();// 获取当前同步状态int c = getState();// 如果有线程持有写锁,则返回-1if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)return -1;// 获取持有锁的线程数int r = sharedCount(c);// 判断是否阻塞,判断线程数量,判断CAS设置是否成功if (!readerShouldBlock() &&r < MAX_COUNT &&compareAndSetState(c, c + SHARED_UNIT)) {// 当前线程是第一个获取到锁的线程if (r == 0) {firstReader = current;firstReaderHoldCount = 1;// 否则就++} else if (firstReader == current) {firstReaderHoldCount++;} else {  HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;}return 1;}// 死循环去获取锁return fullTryAcquireShared(current);}
  • protected boolean tryReleaseShared(int arg):共享式释放同步状态。
protected final boolean tryReleaseShared(int unused) {Thread current = Thread.currentThread();// 对应上面获取锁来读就好了if (firstReader == current) {// assert firstReaderHoldCount > 0;if (firstReaderHoldCount == 1)firstReader = null;elsefirstReaderHoldCount--;} else {HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))rh = readHolds.get();int count = rh.count;if (count <= 1) {readHolds.remove();if (count <= 0)throw unmatchedUnlockException();}--rh.count;}for (;;) {int c = getState();int nextc = c - SHARED_UNIT;if (compareAndSetState(c, nextc))// Releasing the read lock has no effect on readers,// but it may allow waiting writers to proceed if// both read and write locks are now free.return nextc == 0;}}

上面例子,因为读写锁是共享锁,可重入锁是独占锁,而同步器对于共享锁和独占锁都提供了可重写的方法来获取锁或者释放锁,所以分了两个例子来写。

4 同步器提供的模版方法

  • void acquire(int arg):独占式获取同步状态,如果当前线程获取同步状态成功则返回,否则,将会进入同步队列等待。
public final void acquire(int arg) {// 如果获取锁失败,则加入同步队列,如果加入同步队列成功则自旋阻塞唤醒来不断的尝试获取锁,直到线程被中断或获取到锁if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
  • void acquireInterruptibly(int arg):与上面acquire相似,但是当前方法如果在获取锁的过程中线程中断会抛出InterruptedException并返回。
public final void acquireInterruptibly(int arg) throws InterruptedException {// 线程中断抛出异常if (Thread.interrupted())throw new InterruptedException();// 如果没有获取到锁if (!tryAcquire(arg))// 不断自旋尝试获取锁doAcquireInterruptibly(arg);}
  • boolean tryAcquireNanos(int arg, long nanosTimeout):在acquireInterruptibly()方法的基础上增加了超时时间,如果在超时时间内获取到了锁,则返回true,否则返回false。
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {// 中断抛异常if (Thread.interrupted())throw new InterruptedException();// 相应时间内不断获取锁,超时返回falsereturn tryAcquire(arg) ||doAcquireNanos(arg, nanosTimeout);}
  • void acquireShared(int arg):共享式的获取同步状态,如果当前线程未获取到同步状态,则会进入同步队列等待,与独占锁的主要区别是在同一时刻可以有多少个线程获取到同步状态。
public final void acquireShared(int arg) {// 如果获取锁失败,则不断自旋尝试获取锁,tryAcquireShared方法在上面有讲if (tryAcquireShared(arg) < 0)doAcquireShared(arg);}
  • void acquireSharedInterruptibly(int arg):与acquireShared方法相似,只是如果线程中断,当前方法会抛出异常。
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {// 中断抛出异常if (Thread.interrupted())throw new InterruptedException();// 如果获取锁失败,则不断自旋尝试获取锁if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);}
  • boolean tryAcquireSharedNanos(int arg, long nanosTimeout):在acquireSharedInterruptibly方法的基础上增加了超时时间。
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {// 中断抛出异常if (Thread.interrupted())throw new InterruptedException();// 在超时时间内如果获取锁失败,则不断自旋尝试获取锁return tryAcquireShared(arg) >= 0 ||doAcquireSharedNanos(arg, nanosTimeout);}
  • boolean release(int arg):独占式释放同步状态,该方法在释放同步状态之后,会将队列中的第一个节点包含的线程唤醒。
public final boolean release(int arg) {// 如果获取锁成功if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)// 唤醒第一个节点的线程unparkSuccessor(h);return true;}return false;}
  • boolean releaseShared(int arg):共享式释放同步状态。
public final boolean releaseShared(int arg) {// 如果释放锁成功if (tryReleaseShared(arg)) {// 唤醒线程doReleaseShared();return true;}return false;}
  • Collection getQueuedThreads():获取等待在同步队列上的线程集合。
public final Collection<Thread> getQueuedThreads() {ArrayList<Thread> list = new ArrayList<Thread>();for (Node p = tail; p != null; p = p.prev) {Thread t = p.thread;if (t != null)list.add(t);}return list;}

同步器提供的模板方法基本是分为3类:

  • 独占式获取与释放同步状态
  • 共享式获取与释放同步状态
  • 查询同步队列中的等待线程集合

5 根据同步器自定义同步组件

上面介绍了一些AQS提供的可重写方法和模板方法,接下来我们自定义一个独占锁(在同一时刻只有一个线程能获取锁,其他获取锁的线程只能处于同步队列中,当获取到锁的线程释放锁之后,后面的线程才能够获取锁)

public class ExclusiveLock implements Lock {/*** 自定义同步器*/private static class Sync extends AbstractQueuedSynchronizer{// 判断是否处于占用状态@Overrideprotected boolean isHeldExclusively(){return getState() == 1;}// 加锁@Overrideprotected boolean tryAcquire(int arg) {// 通过CAS设置同步状态(设置成功返回true 设置失败返回false)if (compareAndSetState(0, 1)){setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}// 释放锁@SneakyThrows@Overrideprotected boolean tryRelease(int arg) {// 如果同步状态为未获取锁,则抛出异常,没有线程获取到锁,不能释放锁if (getState() == 0){throw new IllegalAccessException();}// 释放锁setExclusiveOwnerThread(null);setState(0);return true;}// 返回一个Condition,每个Condition都包含一个Condition队列protected Condition newCondition() {return new ConditionObject();}}private final Sync sync = new Sync();@Overridepublic void lock() {// 独占式加锁sync.acquire(1);}@Overridepublic void lockInterruptibly() throws InterruptedException {// 加锁线程中断抛出异常,否则自旋加锁sync.acquireInterruptibly(1);}@Overridepublic boolean tryLock() {// 加锁成功返回true,否则设置占用排它锁的线程是当前线程return sync.tryAcquire(1);}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {// 加锁线程中断抛出异常,否则在有效时间内尝试自旋加锁return sync.tryAcquireSharedNanos(1, unit.toNanos(time));}@Overridepublic void unlock() {// 释放锁sync.release(1);}@Overridepublic Condition newCondition() {// 返回Conditionreturn sync.newCondition();}
}

如上代码,我们自定义了一个独占锁,它在同一时刻只允许一个线程占有锁。sync内部类继承了同步器并实现了独占式获取和释放同步状态。在tryAcquire(int arg)方法中,如果通过CAS设置成功,则代表获取了同步状态,而在tryRelease(int arg)方法中只是将同步状态重制为0。用户在使用ExclusiveLock时并不会直接和内部同步器打交道,而是调用ExclusiveLock提供的方法即可,如加锁调用lock()方法,如果获取锁失败则会被加入同步队列中,释放锁调用unlock()方法,如果没有线程获取锁的时候释放锁会抛出异常,还可以按指定时间尝试获取锁等等。

结尾

本来想把同步器实现原理也写一些的,结果看了一下篇幅好想有些许长,那就分两篇来写把,如果看完感觉有帮助的,请帮忙点个赞, ,有缘下篇文章再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

递归树

用于可视化递归算法的流程。当你知道递归的时间复杂度的公式后&#xff0c;就可以画出递归树&#xff0c;有利于你计算递归算法的时间复杂度。 像这种公式&#xff0c;第一个2说明是二叉树&#xff0c;一分为2&#xff1b;第二个n/2&#xff0c;说明他的儿子们所占用的数据只有…

实例1:python(续)

#有四个数字&#xff1a;1、2、3、4&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;各是多少&#xff1f; d[] for a in range(1,5): for b in range(1,5): for c in range(1,5): if (a!b) and (a!c) and (c!b): d.append([a,b,c]) print (“总数量&#xf…

实例2:python

#企业发放的奖金根据利润提成。利润(I)低于或等于10万元时&#xff0c;奖金可提10%&#xff1b;利润高于10万元&#xff0c;低于20万元时&#xff0c;低于10万元的部分按10%提成&#xff0c;高于10万元的部分&#xff0c;可提成7.5%&#xff1b;20万到40万之间时&#xff0c;高…

java快速排序直观演示代码,排序算法总结(含动图演示和Java代码实现)

本文将围绕冒泡排序、桶排序、计数排序、堆排序、插入排序、并归排序、快速排序和选择排序&#xff0c;按照描述、时间复杂度(最坏情况)、动态图展示和代码实现来讲解。本文默认排序为从小到大。本文相关代码已上传至github&#xff0c;欢迎关注https://github.com/zhuzhenke/c…

scrapy ip地址 tcp time out_TCP的运作流程(一)——“三次握手”

前言看过前面有关两篇HTTP的文章的同学&#xff0c;想必对HTTP已经有了一定的了解。在HTTP初始(一)中提到过TCP/IP四层网络模型&#xff0c;这次我们就来详细了解一下TCP传输。因为时间和篇幅所限&#xff0c;本篇讲分为两章&#xff0c;本章讲TCP的三次握手&#xff0c;下章讲…

c++,c.c#区别

C则一般看作是对C语言的扩展。 Java语言是一种完全的面向对象语言&#xff0c;虽然它的底层&#xff08;运行时库&#xff09;是用C语言开发的&#xff0c;可是并不依赖于C。 C#是微软开发的一种编程语言&#xff0c;语法类似Java&#xff0c;几乎就是从Java的翻版。 &#xff…

php.ini 老薛,出现Allowed memory size of 134217728 bytes exhausted怎么办?

有站长在交流群内说到使用 ZBlogPHP 建站在发布文章的时候总是出现以下错误&#xff1a;Allowed memory size of 134217728 bytes exhausted(tried to allocate 12288 bytes)允许耗尽内存大小为 134217728 字节(试图分配 12288 字节)具体如下图所示&#xff1a;134217728 bytes…

winform窗体

WinForm是Net开发平台中对Windows Form的一种称谓。 中文名 Winform 特 点 功能强大 操作方便 新的数据提供程序管理 使用安全 利用公共语言运行库的安全特性 特点 Windows窗体的一些重要特点如下&#xff1a; 功能强大 Windows窗体可用于设计窗体和可视控件&#xff0c;以创建…

实例3:python

#一个整数&#xff0c;它加上100后是一个完全平方数&#xff0c;再加上168又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; #假设该数为 x。 #1、则&#xff1a;x 100 n2, x 100 168 m2 #2、计算等式&#xff1a;m2 - n2 (m n)(m - n) 168 #3、设置&…

实例4:python

#输入某年某月某日&#xff0c;判断这一天是这一年的第几天&#xff1f; #!/usr/bin/python -- coding: UTF-8 -- #!/usr/bin/python -- coding: UTF-8 -- yearint(2019) monthint(8) dayint(13) months1[0,31,60,91,121,152,182,213,244,274,305,335,366] #闰年 months2[…

php html登陆逻辑,保持演示文稿(HTML)和逻辑(PHP)分开

我试图保持演示和逻辑分离,而不使用像Smarty这样的模板引擎.到目前为止我所做的工作是有效的,但我不知道如果不在我的演示文稿中添加更多PHP而不是我想做的事情.例如,现在我有这样的事情&#xff1a;product_list.phptry {$query $conn->prepare("SELECT p.id,p.name,…

层次分析法matlab_建模开讲课程回放2:层次分析法及其MATLAB

建模开讲&#xff1a;层次分析法及其MATLAB实践主讲人&#xff1a;于晶贤老师课程回放地址如下&#xff0c;大家可以复制链接到地址栏即可观看&#xff0c;也可以直接点击左下角的观看&#xff1a;https://ke.qq.com/webcourse/index.html?fromqqchat&cid493154&term_…

raw input()和input区别

版本差异 raw_input——》python2版本 input——》python3版本 2. 输入格式差异 就是raw_input()随便输都是字符串&#xff0c;而input()必须按照Python的规则来~ raw_input() nameraw_input(‘输入姓名&#xff1a;’) ageraw_input(‘输入年龄’) 我们输入汉字的姓名和数…

不同vlan之间如何ping通_【丰润达.安防百科】如何实现交换机不同VLAN、不同网段之间互访?...

交换机如何实现不同网段的互访&#xff1f;这个交换机完全可以实现&#xff0c;在实际项目中&#xff0c;交换机实现不同网段的互访用的也比较多&#xff0c;那么今天我们一起来看下。▎同一个vlan中&#xff0c;不同网段的主机如何互通同一个vlan&#xff0c;不同网段的主机如…

实例5:python

#输入三个整数x,y,z&#xff0c;请把这三个数由小到大输出。 l [] for i in range(3): x int(input(‘integer:\n’)) l.append(x) l.sort() print (l)

背景图层和普通图层的区别_008Photoshop四赞图层(图层样式)

图层过滤器图层滤镜&#xff0c;比如你只想看到像素图层&#xff0c;选择像素图层过滤器&#xff0c;这时就只能看到背景图层了(在图层面板)&#xff0c;如果再点击像素图层过滤器&#xff0c;其他图层又出现了。选中调整图层过滤器&#xff0c;就只能看到图层结构里的调整图层…

实例6:python

#斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;指的是这样一个数列&#xff1a;0、1、1、2、3、5、8、13、21、34、……。 #F0 0 (n0) #F1 1 (n1) #Fn F[n-1] Fn-2 #!/usr/bin/python -- coding: UTF-8 -- #def fib(…

vue devtools面板没有显示_vue 基础入门(四)

vue 基础入门(四)1.全局配置Vue.config 是一个对象&#xff0c;包含 Vue 的全局配置。可以在启动应用之前修改下列 property&#xff1a;1.1 silent类型&#xff1a;boolean默认值&#xff1a;false用法&#xff1a;Vue.config.silent true❝取消 Vue 所有的日志与警告。❞1.2…

vue 生命周期_Vue生命周期小白看了都会的

最近一直在学习Vue&#xff0c;而vue生命周期是我们不可能绕开的一个很核心的知识点&#xff0c;今天来简单的梳理一下大概的内容。一、钩子函数在一开始学习的时候&#xff0c;总有钩子函数这个名词冒出来&#xff0c;而且在vue官网文档中也频繁出现&#xff0c;也相信给很多初…

实例7:python

#将一个列表的数据复制到另一个列表中。 #程序分析&#xff1a;使用列表[:]。 #程序源代码&#xff1a; #!/usr/bin/python -- coding: UTF-8 -- #a [1, 2, 3] #b a[:] #print (b) #import copy #a [1, 2, 3] #bcopy.copy(a) #print(b) #[1, 2, 3] #使用 Python3 的参…