多线程的这些锁知道吗?手写一个自旋锁?

多线程中的各种锁 1. 公平锁、非公平锁 1.1 概念:

公平锁就是先来后到、非公平锁就是允许加塞 Lock lock = new ReentrantLock(Boolean fair); 默认非公平

公平锁是指多个线程按照申请锁的顺序来获取锁,类似排队打饭。

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者节现象。

1.2 两者区别?

公平锁:

Threads acquire a fair lock in the order in which they requested it

公平锁,就是很公平,在并发环境中,每个线程在获取锁时,会先查看此锁维护的等待队列,如果为空,或者当前线程就是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己

非公平锁:

a nonfair lock permits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lock happens to be available when it is requested

非公平锁比较粗鲁,上来就直接尝试占有额,如果尝试失败,就再采用类似公平锁那种方式。

1.3 如何体现公平非公平?

对Java ReentrantLock而言,通过构造函数指定该锁是否公平,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大

对Synchronized而言,是一种非公平锁

image-20210707143515840

其中ReentrantLock和Synchronized默认都是非公平锁,默认都是可重入锁

2. 可重入锁(递归锁)

前文提到ReentrantLock和Synchronized默认都是非公平锁,默认都是可重入锁,那么什么是可重入锁?

2.1 概念

指的时同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块

2.2 为什么要用到可重入锁?

可重入锁最大的作用是避免死锁

ReentrantLock/Synchronized 就是一个典型的可重入锁

2.3 代码验证可重入锁?

首先我们先验证ReentrantLock

package com.yuxue.juc.lockDemo; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 尝试验证ReentrantLock锁的可重入性的Demo * */ public class ReentrantLockDemo { public static void main(String[] args) { mobile mobile = new mobile(); new Thread(mobile,"t1").start(); new Thread(mobile,"t2").start(); } } /** * 辅助类mobile,首先继承了Runnable接口,可以重写run方法 * 内部主要有两个方法 * run方法首先调用第一个方法 * */ class mobile implements Runnable { Lock lock = new ReentrantLock(); //run方法首先调用第一个方法 @Override public void run() { testMethod01(); } //第一个方法,目的是首先让线程进入方法1 public void testMethod01() { //加锁 lock.lock(); try { //验证线程进入方法1 System.out.println(Thread.currentThread().getName() + "\t" + "get in the method1"); //休眠 Thread.sleep(2000); //进入方法2 testMethod02(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //第二个方法。目的是验证ReentrantLock是否是可重入锁 public void testMethod02() { lock.lock(); try { System.out.println("==========" + Thread.currentThread().getName() + "\t" + "leave the method1 get in the method2"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }

因为同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块

之后观察输出结果为:

t1 get in the method1 ==========t1 leave the method1 get in the method2 t2 get in the method1 ==========t2 leave the method1 get in the method2

意味着线程t1进入方法1之后,再进入方法2,也就是说进入内层方法自动获取锁,之后释放方法2的那把锁,再释放方法1的那把锁,这之后线程t2才能获取到方法1的锁,才可以进入方法1

同样地,如果我们在方法1中再加一把锁,不给其解锁,也就是

image-20210707153757001

那么结果会是怎么呢?我们运行代码可以得到

image-20210707153858881

我们发现线程是停不下来的,线程t1进入方法1加了两把锁,之后进入t2,但是退出t1的方法过程中没有解锁,这就导致了t2线程无法拿到锁,也就验证了锁重入的问题

那么为了验证是同一把锁,我们在方法1对其加锁两次,方法2对其解锁两次可以吗?这锁是相同的吗?也就意味着:

image-20210707154054167

我们再次运行,发现结果为:

t1 get in the method1 ==========t1 leave the method1 get in the method2 t2 get in the method1 ==========t2 leave the method1 get in the method2

也就侧面验证了加锁的是同一把锁,更验证了我们的锁重入问题

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzfjf.html