From 4424892ed2228e0e9da297ba7221086164202d2e Mon Sep 17 00:00:00 2001 From: laok <244704761@qq.com> Date: Wed, 1 Jan 2020 23:42:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=80=81K=E7=AC=AC=E4=B8=89=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...6\237\347\220\206\350\247\243\346\236\220" | 37 +++++++++++++++++++ ...6\237\347\220\206\345\210\206\346\236\220" | 19 ++++++++++ ...2\220\347\240\201\345\210\206\346\236\220" | 7 ++++ 3 files changed, 63 insertions(+) create mode 100644 "week_03/49/AQS\345\216\237\347\220\206\350\247\243\346\236\220" create mode 100644 "week_03/49/ReentrantLock\345\216\237\347\220\206\345\210\206\346\236\220" create mode 100644 "week_03/49/Semaphore\346\272\220\347\240\201\345\210\206\346\236\220" diff --git "a/week_03/49/AQS\345\216\237\347\220\206\350\247\243\346\236\220" "b/week_03/49/AQS\345\216\237\347\220\206\350\247\243\346\236\220" new file mode 100644 index 0000000..d56270d --- /dev/null +++ "b/week_03/49/AQS\345\216\237\347\220\206\350\247\243\346\236\220" @@ -0,0 +1,37 @@ +AbstractQueuedSynchronizer的父类是AbstractOwnableSynchronizer。 +AbstractOwnableSynchronizer类功能很单一:只是set和get独占资源线程。 +什么是独占独占资源线程?比如:ReentrantLock可重入锁的第一个调用lock方法的线程,这个线程锁住资源,其他线程调用lock方法只能等待,那么这个线程就是独占资源线程。 + +AQS的大概原理和流程: +1、维护了一个state变量和node双向fifo链表。 +已获取资源占有许可的数量。比如:如果线程调用acquire(1)请求资源许可,acquire会调用一次tryAcquire(1)尝试获取资源。 +如果获取成功:则state加1,并调用AQS的父类AbstractOwnableSynchronizer的设置独占线程,把当前独占线程设置当前线程。 +如果调用失败:则说明,前面已经有线程占用了这个资源,需要等待的线程释放。则把当前线程封装成node节点,放入node双向链表,之后Locksupport.pack()堵塞当前线程。 +加入这个线程堵塞后被唤醒,则继续循环调用tryAcquire方法获取资源许可,获取到了,则把自身node节点设置为node链表的头节点,把之前的头节点去掉。 +如果线程释放资源,调用release方法,release方法会调用tryRelease方法尝试释放资源,如果释放成功,则state减1,再调用AQS的父类AbstractOwnableSynchronizer的设置独占线程为null,再locksupport.unpack()双向node链表的头node节点的线程,恢复其执行。 + +2、维护了一个condition单向链表。 +condition是一个接口,AQS的内部类conditionObject实现了这个接口。这个接口的作用类似于实现java Object的wait方法,notify方法。 +当然condition是内部的数据结构进行Locksupport.pack和unpack来实现的,而且提供了超时机制。 +condition并不是AQS维护,只有当需要await和signal机制,通过new ConditionObject()生成了这个对象,调用这个对象的方法进行使用,AQS本身不存在这个对象的引用,condition单链表也是conditionObject内部类本身在维护,而不是AQS在维护。 +调用condition.await()方法,意味着当前线程进入等待状态。将当前节点包装成node节点,放入condition链表的尾部。然后调用AQS的release方法,释放state,locksupport.unpack()双向node链表的头结点线程。之后,再将自身线程堵塞。 +调用condition.signal()方法,意味着唤醒其他线程调用condition.await()方法进入等待状态的线程。会将conditionObject维护的node单向链表的头节点,移动到AQS维护的双向node节点的尾部,等待其他线程使用完资源后调用release方法一个一个唤醒。 + +AQS的内部类Node: +node类,包装了线程,双向链表的前后引用,节点的几种状态和当前节点的状态。 +cancelled:代表取消,和线程有关,暂时不考虑线程中断。 +signal:代表此节点的next节点可以被唤醒,node的双链表,除了第一次构造node双链表的时候head节点为逻辑null节点,其他时候,head节点都是正在运行的线程所对应的节点,唤醒的也都是head节点的next节点。 +condition:代表是调用condition.signal方法调用之后,从condition单向node链表中移过来的节点。也是可以唤醒的节点。 + +acquire方法介绍:如果获取成功:则state加1,并调用AQS的父类AbstractOwnableSynchronizer的设置独占线程,把当前独占线程设置当前线程。 +如果调用失败:则说明,前面已经有线程占用了这个资源,需要等待的线程释放。则把当前线程封装成node节点,放入node双向链表,之后Locksupport.pack()堵塞当前线程。 +加入这个线程堵塞后被唤醒,则继续循环调用tryAcquire方法获取资源许可,获取到了,则把自身node节点设置为node链表的头节点,把之前的头节点去掉。 +node节点的waitStatus为signal,则意味这其next节点可以被唤醒。 + +release方法介绍:如果线程释放资源,调用release方法,release方法会调用tryRelease方法尝试释放资源,如果释放成功,tryRelease方法会将state减1, +再调用AQS的父类AbstractOwnableSynchronizer的设置独占线程为null,再locksupport.unpack()双向node链表的头node节点的线程,恢复其执行 + +await方法介绍:调用condition.await()方法,意味着当前线程进入等待状态。将当前节点包装成node节点,放入condition链表的尾部。 +然后调用AQS的release方法,释放state,locksupport.unpack()双向node链表的头结点线程。之后,再将自身线程堵塞 + + diff --git "a/week_03/49/ReentrantLock\345\216\237\347\220\206\345\210\206\346\236\220" "b/week_03/49/ReentrantLock\345\216\237\347\220\206\345\210\206\346\236\220" new file mode 100644 index 0000000..8a40149 --- /dev/null +++ "b/week_03/49/ReentrantLock\345\216\237\347\220\206\345\210\206\346\236\220" @@ -0,0 +1,19 @@ +ReentrantLock是Java并发包中提供的一个可重入的互斥锁。ReentrantLock和synchronized在基本用法,行为语义上都是类似的,同样都具有可重入性。 +只不过相比原生的Synchronized,ReentrantLock增加了一些高级的扩展功能,比如它可以实现公平锁,同时也可以绑定多个Conditon。 + +可重入性: +所谓的可重入性,就是可以支持一个线程对锁的重复获取,原生的synchronized就具有可重入性,一个用synchronized修饰的递归方法,当线程在执行期间,它是可以反复获取到锁的, +而不会出现自己把自己锁死的情况。ReentrantLock也是如此,在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。 + +公平锁/非公平锁: +所谓公平锁,顾名思义,意指锁的获取策略相对公平,当多个线程在获取同一个锁时,必须按照锁的申请时间来依次获得锁,排排队,不能插队; +非公平锁则不同,当锁被释放时,等待中的线程均有机会获得锁。synchronized是非公平锁,ReentrantLock默认也是非公平的, +但是可以通过带boolean参数的构造方法指定使用公平锁,但非公平锁的性能一般要优于公平锁。 +synchronized是Java原生的互斥同步锁,使用方便,对于synchronized修饰的方法或同步块,无需再显式释放锁。 +synchronized底层是通过monitorenter和monitorexit两个字节码指令来实现加锁解锁操作的。而ReentrantLock做为API层面的互斥锁,需要显式地去加锁解锁。 + +方法介绍: +1、无参构造器(默认为非公平锁)ReentrantLock():sync是ReentrantLock内部实现的一个同步组件,它是Reentrantlock的一个静态内部类,继承于AQS +2、带布尔值的构造器(是否公平)ReentrantLock(boolean fair):处可以指定是否采用公平锁,FailSync和NonFailSync亦为Reentrantlock的静态内部类,都继承于Sync。 +3、lock():Sync的lock方法是抽象的,实际的lock会代理到FairSync或是NonFairSync上(根据用户的选择来决定,公平锁还是非公平锁) +4、unlock():释放锁,调用sync的release方法,其实是AQS的release逻辑。 \ No newline at end of file diff --git "a/week_03/49/Semaphore\346\272\220\347\240\201\345\210\206\346\236\220" "b/week_03/49/Semaphore\346\272\220\347\240\201\345\210\206\346\236\220" new file mode 100644 index 0000000..b0349b0 --- /dev/null +++ "b/week_03/49/Semaphore\346\272\220\347\240\201\345\210\206\346\236\220" @@ -0,0 +1,7 @@ +在Java的并发包中,Semaphore类表示信号量。Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理。 +Semaphore有两个构造函数,参数permits表示许可数,它最后传递给了AQS的state值。 +线程在运行时首先获取许可,如果成功,许可数就减1,线程运行,当线程运行结束就释放许可,许可数就加1。 +如果许可数为0,则获取失败,线程位于AQS的等待队列中,它会被其它释放许可的线程唤醒。 +在创建Semaphore对象的时候还可以指定它的公平性。 +一般常用非公平的信号量,非公平信号量是指在获取许可时先尝试获取许可,而不必关心是否已有需要获取许可的线程位于等待队列中,如果获取失败,才会入列。 +而公平的信号量在获取许可时首先要查看等待队列中是否已有线程,如果有则入列。 -- Gitee