悲观锁与乐观锁

2020-12-03 19:31 Android编程精选

Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

扫码关注添加客服

进Python社群▲

扫码关注添加客服

进Java社群


作者丨张潮州

来源丨Android技术堆栈

悲观锁与乐观锁

悲观锁和乐观锁,是保证数据并发安全防止更新丢失的两种方法。

乐观锁和悲观锁

1、多线程并发,数据更新丢失

静态变量sCount线程不安全:

public class NoLock {    private static int sCount = 0;
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //线程A让sCount自增100次 for (int i = 0; i < 100; i++) { sCount++; } } }, "A").start();
new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //线程B让sCount自增100次 for (int i = 0; i < 100; i++) { sCount++; } } }, "B").start();
try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println("Main" + sCount); }}

运行结果可能:

Main103

或可能:

Main200

2、悲观锁 Pessimistic Lock

认为程序并发严重,在读取数据的时候,认为其他线程会修改此数据,故加锁防止其他线程修改。例子:使用synchronized加锁,保证数据线程安全。

public class PessimisticLock {    private static int sCount = 0;
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //线程A让sCount自增100次 for (int i = 0; i < 100; i++) { synchronized (PessimisticLock.class){ sCount++; } } } }, "A").start();
new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //线程B让sCount自增100次 for (int i = 0; i < 100; i++) { synchronized (PessimisticLock.class){ sCount++; } } } }, "B").start();
try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println("Main" + sCount); }}

运行结果:

Main200

2.1、synchronized缺点

synchronized加锁确保了线程安全,但却是以消耗性能为代价的,synchronized会让没有得到锁资源的线程进入BLOCKED状态,当争夺到锁资源后恢复为RUNNABLE状态,此过程涉及操作系统用户模式和内核模式的转换,代价较高。

3、乐观锁 Optimistic Lock

认为程序并发不严重,在读取数据的时候,其他线程不会修改,故不加锁;但在更新数据的时候,会判断其他线程是否更新此数据,如果失败会让线程不断去尝试更新。例子:使用原子操作类AtomicInteger的底层实现CAS机制。

public class OptimisticLock {    private static AtomicInteger sCount = new AtomicInteger(0);
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //线程A让sCount自增100次 for (int i = 0; i < 100; i++) { sCount.incrementAndGet(); } } }, "A").start();
new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //线程B让sCount自增100次 for (int i = 0; i < 100; i++) { sCount.incrementAndGet(); } } }, "B").start();
try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println("Main" + sCount); }}

运行结果:

Main200

3.1、CAS机制

CAS机制,Compare And Swap,即比较并替换,使用基本操作数:内存地址,旧的预期值,将修改的新值。更新一个变量的时候,只有当变量的预期值和内存地址当中的实际值相同时,才会将内存地址对应的值修改为新值。

3.2、CAS自旋

当变量的预期值与内存地址当中的实际值不相同时,线程会重新获取内存地址的当前值,并重新计算将要修改的新值,这种尝试的过程被称为自旋。

3.3、CAS缺点

CAS的缺点解释
CPU开销较大在并发量比较高的情况下,如果多线程反复尝试更新某变量,会给CPU带来很大压力。
无法保证代码块的原子性只能保证一个变量的原子性操作,而不能像synchronized一样保证整个代码块的原子性
程序员专栏
 扫码关注填加客服 
长按识别下方二维码进群

近期精彩内容推荐:  

 员工因上厕所时间超长被开除了

 程序员连续15天加班到凌晨2点在餐厅泪崩!

 还在try...catch?如果是那你就out了!

 Python很慢?Python之父一句话亮了





在看点这里好文分享给更多人↓↓

本文章转载自公众号:AndroidPush

首页 - Android 相关的更多文章:
888