JUC并发编程——各种锁的理解(基于狂神说的学习笔记)

编程入门 行业动态 更新时间:2024-10-27 02:17:34

JUC并发编程——各种锁的理解(基于狂神说的<a href=https://www.elefans.com/category/jswz/34/1770117.html style=学习笔记)"/>

JUC并发编程——各种锁的理解(基于狂神说的学习笔记)

各种锁的理解

公平锁与非公平锁

公平锁:非常公平,不能够插队,先来后到

非公平锁:可以插队,比较灵活(默认都是非公平,如:synchronized,lock)

// Lock lock = new ReentrantLock(); 不带参数的构造方法
public ReentrantLock() {sync = new NonfairSync();
}
// Lock lock = new ReentrantLock(true); 带参数的构造方法   
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

可重入锁(递归锁)

当一个锁中还有一个锁时,线程不会放弃第一把锁,就像递归一样层层深入,一直持有锁。

可以类比成:当你拿到进入一个房子的锁,进入房子后,再拿到进入卧室的锁,进入卧室,此时,就算你已经拿到了卧室的锁,你也并没有放弃房子的锁,别人依然进不来房子。

synchronized版

package lock;// Synchronized
public class Demo01 {public static void main(String[] args) {phone phone = new phone();new Thread(()->{phone.sms();},"A").start();new Thread(()->{phone.sms();},"B").start();}
}class phone{public synchronized void sms(){System.out.println(Thread.currentThread().getName()+"sms");call();}public synchronized void call(){System.out.println(Thread.currentThread().getName()+"call");}
}

lock 版

package lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Demo02 {public static void main(String[] args) {phone2 phone2 = new phone2();new Thread(()->{phone2.sms();},"A").start();new Thread(()->{phone2.sms();},"B").start();}
}class phone2{Lock lock = new ReentrantLock();public void sms(){lock.lock();try {System.out.println(Thread.currentThread().getName()+"sms");call();// 这里也有锁} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}public void call(){lock.lock();// 锁必须配对,否则会死锁try {System.out.println(Thread.currentThread().getName()+"call");} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}
}

运行结果如下:

从结果可以看到,即使A线程进入了call方法,也依然没有放弃sms方法的锁,B线程依然无法进入sms方法

自旋锁

一个自旋锁的源代码示例

public final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset);} while (!weakCompareAndSetInt(o, offset, v, v + delta));return v;
}

自旋锁示例:

设计一个自旋锁:

package lock;import java.util.concurrent.atomic.AtomicReference;/*** 自旋锁*/
public class Demo03 {AtomicReference<Thread> atomicReference = new AtomicReference<>();// 加锁public void myLock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"-->my lock");while(!atomicReferencepareAndSet(null,thread)){}System.out.println(Thread.currentThread().getName()+"--->has this lock");}// 解锁public void myUnlock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"-->my unlock");atomicReferencepareAndSet(thread,null);}
}

该自旋锁示例本质上使用了CAS操作,笔者在狂神说代码上加入了

System.out.println(Thread.currentThread().getName()+"--->has this lock");

测试类:

package lock;import java.util.concurrent.TimeUnit;public class TestDemo03 {public static void main(String[] args) throws InterruptedException {// 底层使用的是CASDemo03 lock = new Demo03();new Thread(()->{lock.myLock();try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {throw new RuntimeException(e);} finally {lock.myUnlock();}},"T1").start();TimeUnit.SECONDS.sleep(1);new Thread(()->{lock.myLock();try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {throw new RuntimeException(e);} finally {lock.myUnlock();}},"T2").start();}}

结果如下:

至此,读者可以看到,T1在打印“has this lock”之后,T2是无法持有该锁,它在不断地自旋,即进入while循环一直判断,我们来看一下这个代码在做什么

while(!atomicReferencepareAndSet(null,thread))

这条代码是actomicReference调用了一个compareAndSet方法(CAS),这个方法是用来判断,如果Thread为Null,则加入修改为thread,并返回true,!true == false,则会退出while循环,如果Thread为某个线程thread,则于CAS的预期值(null)不同,会返回false,!false == true,所以会进入循环,循环体内什么都没有,又会进入判断,周而复始,直到那个线程unlock,调用CAS将thread修改为null。

思考:为什么要让锁一直自旋判断呢?

更多推荐

JUC并发编程——各种锁的理解(基于狂神说的学习笔记)

本文发布于:2023-12-06 16:50:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1668208.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:学习笔记   JUC   狂神说

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!