700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 常⻅锁策略(1. 乐观锁 悲观锁2. 公平锁 非公平锁3. 读写锁4. 可重入锁 自旋锁)

常⻅锁策略(1. 乐观锁 悲观锁2. 公平锁 非公平锁3. 读写锁4. 可重入锁 自旋锁)

时间:2022-12-09 09:52:47

相关推荐

常⻅锁策略(1. 乐观锁  悲观锁2. 公平锁  非公平锁3. 读写锁4. 可重入锁  自旋锁)

目录

1. 乐观锁 & 悲观锁

1.1乐观锁定义

1.2 乐观锁实现 -- CAS

1.3悲观锁定义和应⽤

2. 公平锁 & 非公平锁

3. 读写锁

3.1 读写锁

3.2 独占锁

3.3 共享锁

4. 可重入锁 & 自旋锁

4.1 可重入锁

4.2 自旋锁

1. 乐观锁 & 悲观锁

1.1乐观锁定义

乐观锁认为⼀般情况下不会出现冲突,所以只会在更新数据的时候才对冲突进⾏检测,如果检查出来冲突,不做任何修改,如果没有冲突才进行修改。

1.2 乐观锁实现 -- CAS

CAS(Compare And Swap)⽐较并替换,CAS ⽐较并替换的流程是这样的,CAS 中包含了三个操作单位:V(内存值,)、A(预期的旧址)、B(新值),⽐较 ,如果相等的话则将 V的值更换成 B,否则就提示⽤户修改失败,从⽽实现了 CAS 的机制。

举例:

线程1:V = 10, A = 10, B = 12

线程2:V = 10, A = 10, B = 11

线程1,假设还没有开始对比,时间片用完了 ------------》 线程2,V = A ,所以将 B= 11 给V,线程2执

行完 ---------------》线程1,A:10 == V:11,不相等,修改失败 ----------------》线程1,A: 11 B: 12

--------------------》线程1, A: 11 == V : 11 ,所以将 B = 12 的值给V --------------》线程12

CAS使用--非线程安全问题public class CASDemo1 {private static int count = 0;;private final static int MAX_SIZE = 100000;public static void main(String[] args) throws InterruptedException {// 执⾏ i++ 操作Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < MAX_SIZE; i++) {count++;}}});t1.start();// 执⾏ i-- 操作Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < MAX_SIZE; i++) {count--;}}});t2.start();t1.join();t2.join();System.out.println("最终结果:" + count);}}输出:最终结果:646(随机)

CAS应⽤-AtomicIntegerpublic class CASDemo2 {private static int number = 0;private final static int MAX_COUNT = 100000;private static AtomicInteger atomicInteger = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < MAX_COUNT; i++) {atomicInteger.getAndIncrement();//相当于i++// number++;}});t1.start();Thread t2 = new Thread(() -> {for (int i = 0; i < MAX_COUNT; i++) {atomicInteger.getAndDecrement() ; // 相当于--//number--;}});t2.start();t1.join();t2.join();//可以拿到初始化的值System.out.println("最终结果:" + atomicInteger.get());}}输出:最终结果:0

CAS存在ABA问题:

ABA 转账问题,X 给 Y 转账,系统卡顿点击了两次转账按钮,X 原来是 300 ,正常是转完账(100元)还剩下200 ,第⼀次转账成功之后变成了 200 ,此时 Z 给 X ⼜转了 100 元,余额⼜变成了 300 ,第⼆次CAS 判断(300, 300 , 200 )成功,于是⼜扣了 100 元。

ABA问题:public class ABADemo1 {private static AtomicInteger money = new AtomicInteger(100);public static void main(String[] args) throws InterruptedException {// 第 1 次点击转账按钮(-50)Thread t1 = new Thread(() -> {int old_money = money.get(); // 先得到余额// 执行花费 2stry {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}pareAndSet(old_money, old_money - 50);});t1.start();// 第 2 次点击转账按钮(-50)【不小心点击的,因为第一次点击之后没反应,所以不小心又点了一次】Thread t2 = new Thread(() -> {int old_money = money.get(); // 先得到余额pareAndSet(old_money, old_money - 50);});t2.start();// 给账户 +50 元Thread t3 = new Thread(() -> {// 执行花费 1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}int old_money = money.get();pareAndSet(old_money, old_money + 50);});t3.start();t1.join();t2.join();t3.join();System.out.println("最终账号余额:" + money.get());}}输出:最终账号余额:50

ABA问题解决方案:

引入版本号,每次操作之后,让版本 +1,执行的时候判断版本号和值,就可以解决ABA问题

ABA问题解决public class ABADemo2 {private static AtomicStampedReference<Integer> money = new AtomicStampedReference<>(100,0);public static void main(String[] args) throws InterruptedException {// 第 1 次点击转账按钮(-50)Thread t1 = new Thread(() -> {int old_money = money.getReference(); // 先得到余额int version = money.getStamp(); // 得到版本号// 执行花费 2stry {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}pareAndSet(old_money, old_money - 50, version, version + 1);});t1.start();// 第 2 次点击转账按钮(-50)【不小心点击的,因为第一次点击之后没反应,所以不小心又点了一次】Thread t2 = new Thread(() -> {int old_money = money.getReference(); // 先得到余额int version = money.getStamp(); // 得到版本号pareAndSet(old_money, old_money - 50,version, version + 1);});t2.start();// 给账户 +50 元Thread t3 = new Thread(() -> {// 执行花费 1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}int old_money = money.getReference();int version = money.getStamp();pareAndSet(old_money, old_money + 50,version, version + 1);});t3.start();t1.join();t2.join();t3.join();System.out.println("最终账号余额:" + money.getReference());}}输出:最终账号余额:100

1.3悲观锁定义和应⽤

定义:总是假设最坏的情况,每次去拿数据的时候都认为别⼈会修改,所以每次在拿数据的时候都会上锁,这样别⼈想拿这个数据就会阻塞直到它拿到锁。
应⽤:synchronized、Lock 都是悲观锁。

2. 公平锁 & 非公平锁

⾮公平锁:抢占式执⾏,有⼀些先来的任务还在排队,刚好释放锁的时候新来了⼀个任务,此时是随机获得锁的。公平锁:所有任务来了之后先排队,线程空闲之后去任务队列按顺序执⾏最早任务。
⾮公平锁:ReentrantLock lock = new ReentrantLock(false)。如果构造函数不传递参数,则默认⾮公平锁。公平锁:ReentrantLock lock= new ReentrantLock(true)。

3. 读写锁

3.1 读写锁

读写锁(Readers-Writer Lock)顾名思义是⼀把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,⽽写锁则是互斥锁,不允许多个线程同时获得(写锁),并且写操作和读操作也是互斥的,总结来说,读写锁的特点是:读读不互斥、读写互斥、写写互斥。

Java 标准库提供了 ReentrantReadWriteLock 类, 实现了读写锁:

ReentrantReadWriteLock.ReadLock类表示⼀个读锁. 这个对象提供了 lock / unlock ⽅法进⾏加锁解锁。ReentrantReadWriteLock.WriteLock类表示⼀个写锁. 这个对象也提供了 lock / unlock ⽅法进⾏加解锁。

读写锁特别适合于 "频繁读, 不频繁写" 的场景中. (这样的场景其实也是⾮常⼴泛存在的).

读写锁实例:public class ReadWriteLockDemo1 {public static void main(String[] args) {//创建读写锁final ReentrantReadWriteLock readwriteLock = new ReentrantReadWriteLock();//创建读锁final ReentrantReadWriteLock.ReadLock readLock = readwriteLock.readLock();// 创建写锁final ReentrantReadWriteLock.WriteLock writeLock = readwriteLock.writeLock();// 线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(100));//启动线程任务(读操作1)executor.submit(() -> {//加锁操作readLock.lock();try {//执行业务逻辑System.out.println("执行读锁1:" + LocalDateTime.now());TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} finally {//释放锁readLock.unlock();}});//启动线程任务(读操作2)executor.submit(() -> {//加锁操作readLock.lock();try {//执行业务逻辑System.out.println("执行读锁2:" + LocalDateTime.now());TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} finally {//释放锁readLock.unlock();}});// 创建新线程执行写任务executor.submit(() -> {// 加锁writeLock.lock();try {System.out.println("执行写锁1:" + LocalDateTime.now());TimeUnit.SECONDS.sleep(1);} catch (InterruptedException exception) {exception.printStackTrace();} finally {// 释放锁writeLock.unlock();}});// 创建新线程执行写任务executor.submit(() -> {// 加锁writeLock.lock();try {System.out.println("执行写锁2:" + LocalDateTime.now());TimeUnit.SECONDS.sleep(1);} catch (InterruptedException exception) {exception.printStackTrace();} finally {// 释放锁writeLock.unlock();}});}}输出:执行读锁2:-04-18T09:46:16.293执行读锁1:-04-18T09:46:16.293执行写锁1:-04-18T09:46:19.296执行写锁2:-04-18T09:46:20.296

3.2 独占锁

独占锁是指任何时候都只有⼀个线程能执⾏资源操作。

synchronized、Lock。

3.3 共享锁

共享锁指定是可以同时被多个线程读取,但只能被⼀个线程修改。⽐如 Java 中的 ReentrantReadWriteLock 就是共享锁的实现⽅式,它允许⼀个线程进⾏写操作,允许多个线程读操作。

4. 可重入锁 & 自旋锁

4.1 可重入锁

可重⼊锁指的是该线程获取了该锁之后,可以⽆限次的进⼊该锁锁住的代码。

可重入锁:public class ThreadDemo22 {public static void main(String[] args) {synchronized (ThreadDemo22.class) {System.out.println("线程执行进入了方法");synchronized (ThreadDemo22.class) {System.out.println("线程执行又进入了方法");synchronized (ThreadDemo22.class) {System.out.println("线程执行又又进入了方法");}}}}}输出:线程执行进入了方法线程执行又进入了方法线程执行又又进入了方法

4.2 自旋锁

⾃旋锁是指尝试获取锁的线程不会⽴即阻塞,⽽是采⽤循环的⽅式去尝试获取锁,这样的好处是减少线程上下⽂切换的消耗,缺点是循环会消耗 CPU。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。