java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

宅哥聊构架 后端 2024-11-11

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

一、公平锁与非公平锁

1.1 概述

公平锁: 是指多个线程按照申请锁的顺序来获取锁。
非公平锁: 是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象。饥饿现象就是低优先级的线程可能一直拿不到锁,而一直处于等待状态。

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 中的一些公平锁和非公平锁

  1. java 中的 ReentrantLock,默认是非公平锁,当参数 fair 为 true 时,就是公平锁。java
代码解读
复制代码
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
  1. synchronized 也是一种非公平锁。

二、可重入锁与不可重入锁

2.1 概述

可重入锁(也叫做递归锁): 指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。可重入锁最大的作用就是避免死锁。

不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。

2.2 java 中的可重入锁

2.2.1 synchronized 锁java

代码解读
复制代码
class Phone { public synchronized void sendSMS() { System.out.println(Thread.currentThread().getName() + " send SMS..."); sendEmail(); } public synchronized void sendEmail() { System.out.println(Thread.currentThread().getName() + " send email..."); } } public class ReentrantLockDemo { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }, "Thread1").start(); new Thread(() -> { phone.sendSMS(); }, "Thread2").start(); } }

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

2.2.2 ReentrantLockjava

代码解读
复制代码
class Phone implements Runnable { Lock lock = new ReentrantLock(); @Override public void run() { get(); } public void get() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " get method..."); set(); } finally { lock.unlock(); } } public void set() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " set method..."); } finally { lock.unlock(); } } } public class ReentrantLockDemo { public static void main(String[] args) { Phone phone = new Phone(); Thread thread3 = new Thread(phone, "Thread3"); Thread thread4 = new Thread(phone, "Thread4"); thread3.start(); thread4.start(); } }

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

2.3 面试题

使用ReentrantLock时,如果加入两层锁呢,程序是直接报编译错误,还是正常运行,正常运行的话,能得到预期的结果吗?java

代码解读
复制代码
class Phone implements Runnable { // ... public void get() { lock.lock(); lock.lock(); try { System.out.println(Thread.currentThread().getName() + " get method..."); set(); } finally { lock.unlock(); lock.unlock(); } } // ... }

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁当缺少unlock()时(也就是,lockunlock不是一一对应,lockunlock多 ),程序不会报编译错误,但得不到预期的结果,从下面可以看出,程序一直处于运行的状态:

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁当缺少lock()时(也就是,unlocklock多 ),此时,程序也不会报编译错误,控制台也输出了结果,但是抛出了IllegalMonitorStateException异常。

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

三、自旋锁

3.1 概述

自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

3.2 java 中的自旋锁java

代码解读
复制代码
// Unsafe.java public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }

3.3 手写一个自旋锁java

代码解读
复制代码
public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void myLock() { Thread thread = Thread.currentThread(); System.out.println(thread.getName() + " come in..."); while (!atomicReference.compareAndSet(null, thread)) { } } public void myUnLock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(thread.getName() + " come out..."); } public static void main(String[] args) { SpinLockDemo spinLockDemo = new SpinLockDemo(); new Thread(() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } spinLockDemo.myUnLock(); }, "Thread1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } spinLockDemo.myUnLock(); }, "Thread2").start(); } }

四、写锁(独占锁)、读锁(共享锁)和互斥锁

4.1 概述

独占锁: 指该锁一次只能被一个线程所持有。对ReentrantLockSynchronized而言都是独占锁。
共享锁: 指该锁可被多个线程所持有。
ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。

4.2 示例(模拟缓存)

4.2.1 加锁前:

数据写入的时候,被打断:java

代码解读
复制代码
class MyCache { private volatile Map<String, Object> map = new HashMap<>(); public void put(String key, Object value) { System.out.println(Thread.currentThread().getName() + " 正在写入:" + key); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.put(key, value); System.out.println(Thread.currentThread().getName() + " 写入完成"); } public void get(String key) { System.out.println(Thread.currentThread().getName() + " 正在读取"); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } Object result = map.get(key); System.out.println(Thread.currentThread().getName() + " 读取完成:" + result); } } public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(() -> { myCache.put(temp + "", temp + ""); }, String.valueOf(i)).start(); } for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(() -> { myCache.get(temp + ""); }, String.valueOf(i)).start(); } } }

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

4.2.2 加锁后:

写入时正常,不会中断;读取时,可以共享锁。java

代码解读
复制代码
class MyCache { private volatile Map<String, Object> map = new HashMap<>(); private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); public void put(String key, Object value) { rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 正在写入:" + key); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.put(key, value); System.out.println(Thread.currentThread().getName() + " 写入完成"); } catch (Exception e) { e.printStackTrace(); } finally { rwLock.writeLock().unlock(); } } public void get(String key) { rwLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 正在读取"); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } Object result = map.get(key); System.out.println(Thread.currentThread().getName() + " 读取完成:" + result); } catch (Exception e) { e.printStackTrace(); } finally { rwLock.readLock().unlock(); } } }

java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁

转载来源:https://juejin.cn/post/7394790673613553676

Apipost 私有化火热进行中

评论