【面试】你知道Java里有多少种锁吗?(15种锁最全总结) 您所在的位置:网站首页 java里的锁有哪些 【面试】你知道Java里有多少种锁吗?(15种锁最全总结)

【面试】你知道Java里有多少种锁吗?(15种锁最全总结)

2024-01-19 02:53| 来源: 网络整理| 查看: 265

在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:

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 实验室设备网 版权所有