• Java并发编程--锁


    根据分类标准我们把锁分为以下 7 大类别,分别是:

    1. 偏向锁/轻量级锁/重量级锁;
    2. 可重入锁/非可重入锁;
    3. 共享锁/独占锁;
    4. 公平锁/非公平锁;
    5. 悲观锁/乐观锁;
    6. 自旋锁/非自旋锁;
    7. 可中断锁/不可中断锁。

    以上是常见的分类标准,下面我们来逐一介绍它们的含义。

    偏向锁/轻量级锁/重量级锁

    这三种锁特指 synchronized 锁的状态,通过在对象头中的 mark word 来表明锁的状态。

    • 偏向锁

    如果自始至终,对于这把锁都不存在竞争,那么其实就没必要上锁,只需要打个标记就行了,这就是偏向锁的思想。一个对象被初始化后,还没有任何线程来获取它的锁时,那么它就是可偏向的,当有第一个线程来访问它并尝试获取锁的时候,它就将这个线程记录下来,以后如果尝试获取锁的线程正是偏向锁的拥有者,就可以直接获得锁,开销很小,性能最好。

    • 轻量级锁

    JVM 开发者发现在很多情况下,synchronized 中的代码是被多个线程交替执行的,而不是同时执行的,也就是说并不存在实际的竞争,或者是只有短时间的锁竞争,用 CAS 就可以解决,这种情况下,用完全互斥的重量级锁是没必要的。轻量级锁是指当锁原来是偏向锁的时候,被另一个线程访问,说明存在竞争,那么偏向锁就会升级为轻量级锁,线程会通过自旋的形式尝试获取锁,而不会陷入阻塞。

    • 重量级锁

    重量级锁是互斥锁,它是利用操作系统的同步机制实现的,所以开销相对比较大。当多个线程直接有实际竞争,且锁竞争时间长的时候,轻量级锁不能满足需求,锁就会膨胀为重量级锁。重量级锁会让其他申请却拿不到锁的线程进入阻塞状态。

    可重入锁/非可重入锁

    可重入锁指的是线程当前已经持有这把锁的状态下,能在不释放这把锁的情况下再次获取这把锁。

    非可重入锁指的是虽然当前线程持有了这把锁,但是想再次获取这把锁也必须先释放锁后才能再次尝试获取。

    /**
     * methodA 和methodB 都是同步方法,当线程进入 methodA 会获得该类的对象锁,
     * 在 methodA 对方法 methodB 做了调用,但是 methodB 也是同步的,因此该线程需要再次获得该对象锁。这期间其他线程无法获该对象锁。
     * 因此可重入性避免再次获取锁出现死锁的问题。
     */
    public class LockTest {
    
        public synchronized void methodA() {
            System.out.println("methodA");
            methodB();
        }
    
        public synchronized void methodB() {
            System.out.println("methodB");
        }
    }

    共享锁/独占锁

    共享锁指的是同一把锁可以被多个线程同时获得。

    独占锁指的是这把锁只能同时被一个线程获得。

    公平锁/非公平锁

    公平锁的公平的含义在于如果线程现在拿不到这把锁,那么线程就都会进入等待,开始排队,在等待队列里等待时间长的线程会优先拿到这把锁,有先来先得的意思。而非公平锁就不那么“完美”了,它会在一定情况下,忽略掉已经在排队的线程,发生插队现象。

    悲观锁/乐观锁

    悲观锁的概念是在获取资源之前,必须先拿到锁,以便达到“独占”的状态,当前线程在操作资源的时候,其他线程由于不能拿到锁,所以其他线程不能来影响我。
    乐观锁恰恰相反,它并不要求在获取资源前拿到锁,也不会锁住资源,乐观锁利用 CAS 理念,在不独占资源的情况下,完成了对资源的修改。
    悲观锁适合用于并发写入多、临界区代码复杂、竞争激烈等场景,这种场景下悲观锁可以避免大量的无用的反复尝试等消耗。
    乐观锁适用于大部分是读取,少部分是修改的场景,也适合虽然读写都很多,但是并发并不激烈的场景。在这些场景下,乐观锁不加锁的特点能让性能大幅提高。

    自旋锁/非自旋锁

    自旋锁的理念是如果线程拿不到锁,并不直接陷入阻塞或者释放CPU资源,而是开始利用循环不停的尝试获取锁。

    非自旋锁如果拿不到锁,直接放弃。可能会陷入阻塞或排队。

    可中断锁/不可中断锁

    可中断锁在获取锁的过程中,可以在中断去做其他的事情,不需要一直等到获取到锁才离开。ReentrantLock 是一种典型的可中断锁,例如使用 ReentrantLock.lockInterruptibly() 方法在获取锁的过程中不想获取了,那么可以调用 Thread.interrupt() 方法终止线程。

    不可中断锁一旦线程申请了锁,就只能等到拿到锁后才能进行其他的逻辑处理。synchronized 关键字修饰的锁代表不可中断锁。

  • 相关阅读:
    237. Delete Node in a Linked List
    430. Flatten a Multilevel Doubly Linked List
    707. Design Linked List
    83. Remove Duplicates from Sorted List
    160. Intersection of Two Linked Lists
    426. Convert Binary Search Tree to Sorted Doubly Linked List
    142. Linked List Cycle II
    类之间的关系
    初始化块
    明确类和对象
  • 原文地址:https://www.cnblogs.com/xxoome/p/13289846.html
Copyright © 2020-2023  润新知