【面试】你知道Java里有多少种锁吗?(15种锁最全总结) | 您所在的位置:网站首页 › java里的锁有哪些 › 【面试】你知道Java里有多少种锁吗?(15种锁最全总结) |
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下: 1.公平锁 / 非公平锁 2.可重入锁 / 不可重入锁 3.独享锁 / 共享锁 4.互斥锁 / 读写锁 5.乐观锁 / 悲观锁 6.分段锁 7.偏向锁 / 轻量级锁 / 重量级锁 8.自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释。 公平锁 / 非公平锁公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁。 非公平锁 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。 对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。 可重入锁 / 不可重入锁可重入锁 广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁 synchronized void setA() throws Exception{ Thread.sleep(1000); setB(); } synchronized void setB() throws Exception{ Thread.sleep(1000); }上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。 不可重入锁 不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。看到一个经典的讲解,使用自旋锁来模拟一个不可重入锁,代码如下 import java.util.concurrent.atomic.AtomicReference; public class UnreentrantLock { private AtomicReference owner = new AtomicReference(); public void lock() { Thread current = Thread.currentThread(); //这句是很经典的“自旋”语法,AtomicInteger中也有 for (;;) { if (!owner.compareAndSet(null, current)) { return; } } } public void unlock() { Thread current = Thread.currentThread(); owner.compareAndSet(current, null); } }代码也比较简单,使用原子引用来存放线程,同一线程两次调用lock()方法,如果不执行unlock()释放锁的话,第二次调用自旋的时候就会产生死锁,这个锁就不是可重入的,而实际上同一个线程不必每次都去释放锁再来获取锁,这样的调度切换是很耗资源的。 把它变成一个可重入锁: import java.util.concurrent.atomic.AtomicReference; public class UnreentrantLock { private AtomicReference owner = new AtomicReference(); private int state = 0; public void lock() { Thread current = Thread.currentThread(); if (current == owner.get()) { state++; return; } //这句是很经典的“自旋”式语法,AtomicInteger中也有 for (;;) { if (!owner.compareAndSet(null, current)) { return; } } } public void unlock() { Thread current = Thread.currentThread(); if (current == owner.get()) { if (state != 0) { state--; } else { owner.compareAndSet(current, null); } } } }在执行每次操作之前,判断当前锁持有者是否是当前对象,采用state计数,不用每次去释放锁。 ReentrantLock中可重入锁实现 这里看非公平锁的锁获取方法: 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) {// 如果大于0,表示当前线程多次获取了该锁,释放锁通过count减一来模拟 count--; } else {// 如果count==0,可以将锁释放,这样就能保证获取锁的次数与释放锁的次数是一致的了。 cas.compareAndSet(cur, null); } } } } 自旋锁与互斥锁1.自旋锁与互斥锁都是为了实现保护资源共享的机制。 2.无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。 3获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。 自旋锁总结1.自旋锁:线程获取锁的时候,如果锁被其他线程持有,则当前线程将循环等待,直到获取到锁。 2.自旋锁等待期间,线程的状态不会改变,线程一直是用户态并且是活动的(active)。 3.自旋锁如果持有锁的时间太长,则会导致其它等待获取锁的线程耗尽CPU。 4.自旋锁本身无法保证公平性,同时也无法保证可重入性。 5.基于自旋锁,可以实现具备公平性和可重入性质的锁。 |
CopyRight 2018-2019 实验室设备网 版权所有 |