upjmbai5800
upjmbai5800
多线程
多线程、锁、CAS和AQS(3)
全部文章
多线程
J.U.C(2)
Java基础(25)
Java源码阅读(3)
JVM(4)
mybatis(3)
react(1)
Redis(1)
Spring(1)
springMVC(1)
专利(3)
刷题随笔(1)
实习随笔(3)
操作系统(3)
数据库(7)
数据结构与算法(6)
网络(1)
面试问题总结(3)
高并发(2)
归档
标签
去牛客网
登录
/
注册
多线程、锁、CAS和AQS(3)
576 浏览
0 回复
2019-08-22
upjmbai5800
+关注
多线程、锁、CAS和AQS(3)Java中的锁
Java中锁的分类
公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。如果休眠队列中有线程了,则新进入竞争的线程一定要在休眠队列上排队。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。新进入的线程是无视休眠队列直接抢占锁的。因此占有锁的线程放弃锁后,唤醒线程需要时间,此时被唤醒的线程就会与新进入的线程争锁。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized synchronized底层实现原理而言,也是一种非公平锁,不能改变。
可重入锁
可重入锁又名递归
锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
Synchronized和Java ReentrantLock都是可重入锁。可重入锁可一定程度避免死锁。如下:
独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
Synchronized和Java ReentrantLock都是独享锁。
自旋锁
锁的等待者会原地忙等,不停的询问,直到获得锁。采用让当前线程不停地的在循环体内执行实现,当循环的条件被其他线程改变时才能进入临界区。
如何实现可重入锁(ReentrantLock)?
为每个锁关联一个获取计数值和一个所有者线程。当计数值为0时,这个锁就被认为是没有被任何线程持有。当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1。如果同一个线程再次获取这个锁,计数值将递增,而当线程退出同步代码块时,计数器会相应地递减。当计数值为0时,这个锁将被释放。
可重入锁(ReentrantLock)的实现原理
和上面是一样的,细节是ReentrantLock有公平锁模型和非公平锁模型
对于公平锁,如果休眠队列中有线程了,则新进入竞争的线程一定要在休眠队列上排队。
对于非公平锁,新进入的线程
是无视休眠队列直接抢占锁的。因此占有锁的线程放弃锁后,唤醒线程需要时间,此时被唤醒的线程就会与新进入的线程争锁。
Java中concurrent包的实现
volatile变量和CAS机制是concurrent包实现的基石
AQS、非阻塞数据结构和原子变量类都是通过volatile和CAS实现的
实现的模式如下:
首先,声明共享变量为volatile;
然后,使用CAS的原子条件更新来实现线程之间的同步;
同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
AQS(AbstractQueuedSynchronizer)
AQS(AbstractQueuedSynchronizer),抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...。
AQS有以下几种方法:
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。
上面是AQS定义的资源独占方式,其实还有资源共享方式,采用以下两种方法:
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
Java基础
多线程
举报
收藏
赞
评论加载中...