并发编程2 AQS
一、AQS相关面试
1、synchronized 与Rentrantlock 的区别是什么?
Synchronized,java关键字,可以自动去操作加锁和释放锁,不需要手动编码实现。它的底层行为是由jvm实现的。
Rentrantlock需要调用加锁和解锁的方法,它的底层是AQS实现的。
区别 |
Synchronized |
Rentrantlock |
实现方式 |
JVM |
AQS |
是否自动 |
Yes |
no |
获取锁 |
只要资源被锁,会一直等待 |
针对资源被锁,具有多种处理方式 |
释放锁 |
代码执行完毕或者发生异常 |
手动释放 |
锁的状态 |
不能判断 |
通过 isLocked() |
锁的类型 |
可重入,不可中断 |
可重入,可中断 |
锁的公平 |
非公平锁 |
非公平锁和非公平锁 |
2、ReentrantLock的加锁方式有几种?分别是什么?
Lock() 如果锁不可用(没抢到),出入线程调度的目的,当前线程将会被禁用到。处于休眠状态,直到获取锁
Boolean tryLock()
如果获取了锁,那么返回true,如果没有获取锁,返回false
Boolean tryLock(long timeout,TimeUnit unit)
如果获得到锁了,那么返回true。如果没有获得锁,处于线程调用的目的,当前线程将会被禁用到,处于休眠状态,直到发生以下情况:
1)已经获得了锁,之前的锁已经被释放了
2)其它一些线程中断了当前线程
3)时间到了
Void lockInterruptibly()
如果获得到锁了,那么返回true。如果没有获取到锁,出入线程调度的目的,当前的线程将会被禁用到,处于休眠状态,直到发生下面几种情况:
1> 已经获得了锁,之前的锁已经被释放了。
2> 其他一些线程中断了当前的线程,它是支持中断获取锁。
3、什么是xx锁?Xx锁和yy锁有什么区别?
1)公平锁和非公平锁(fairSync/notFairSync)
公平锁:公平的获取锁,按顺序获取锁
非公平锁:与非公平锁相反
2)独占锁和共享锁
独占锁:只有一个线程可以访问;
共享锁:多个线程可以访问;
3)互斥锁和读写锁
互斥锁:reentrantLock
读写锁:read-write-lock
4)乐观锁和悲观锁
乐观锁:乐观,不认为会发生问题,效率会高但实现复杂
悲观锁:认为肯定会发生问题。reentrentLock,Synchronized。效率低,实现简单。
5)偏向锁、轻量级锁、重量级锁
偏向锁——>轻量级锁——>重量级锁 (递增的一个锁的进化状态)
偏向锁:一个代码块, 一直被一个线程所访问。线程就会自动获得锁,降低获得锁的代价
轻量级锁:前提现在的锁是偏向锁,被另一个线程访问。它升级为轻量级锁。
重量级锁:前提,现在是轻量级锁,但是自旋一定次数后,还没有获得这个锁,那么就会进入阻塞状态。这个时候,锁就升级为了重量级锁。
6)重入锁(reentrantLock)
7)分段锁 concurrentHashMap
8)自旋锁
不是马上阻塞,而是采用自旋方式,优点:减少上下文切换。缺点:对CPU有很大的消耗。
4、Synchronized
1) Synchronized修饰代码块,对象锁
2) Synchronized修饰方法,对象锁
3) Synchronized 修饰静态方法,类锁
4) Synchronized 修饰类,类锁
5、什么是AQS
AQS是AbstractQueuedSynchronizer的简称。也就是抽象队列同步器。基于AQS实现的有reentrantlock,reentrantReadWriteLock,Semaphone,countDownLatch,
cyclicBarrier
一、线程扩展工具类
1、CAS是乐观锁,native方法,三个参数,一个期望的值,一个偏移量,一个要修改的值,如果当前值等于期望值,那就修改成功。
2、CountDownLatch
countDownLatch 是一个多线程控制工具。用来控制线程的等待。设置需要countDown的数量num,然后每一个线程执行完毕之后,调用countDown()方法,而主线程调用await()方法执行等待,直到num个子线程执行了countDown()方法,则主线程开始执行。
3、CyclicBarrier
与countDownLatch 非常相似。它支持计数器的反复使用。
CyclicBarrier可以接收一个参数作为barrierAction,每当计数器完成一次技术后——CyclicBarrier.await()时,系统会执行的动作。
4、Semaphore
表示信号量,允许指定数量的线程同时访问某个资源。
acquire()方法用于获得准入许可(如何没有获得成功,则进行等待,直到有线程释放许可而获得该许可为止)
release()用于释放准入许可。
二、AQS源码
1、LockSupport是一个非常好用的线程阻塞工具,它可以在线程内任意位置让线程阻塞。与object.await()方法想比,他不需要获得某个对象的锁,也不会抛出InterruptedException异常
2、Park()可以阻塞当前线程;每个线程有一个许可,该许可默认为不可用,如果该许可是可用状态,那么park()方法会立即返回,消费这个许可。将改许可变为不可用的状态。如果先调用unpark(),使许可证变为可用状态。所以先调用unpark再调用park也不会阻塞。
3、AQS的实现原理是什么?
State表示锁被抢到的次数。加锁+1,释放锁-1。
0:表示没有现成占有该锁
1:有线程成功抢到了该锁
2:执行重入锁了,即:某线程自己抢占了两次
N:表示自己抢占了n次锁
WaitStatus 状态,1cancell 取消
-1 signal 等待被唤醒
2、ReentrantLock
默认是非公平锁,在第一次未抢到锁后,也会去队列里排队,变为公平锁。
3、加锁流程