博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java锁浅析
阅读量:6829 次
发布时间:2019-06-26

本文共 3061 字,大约阅读时间需要 10 分钟。

锁是什么

java开发中进行并发编程时针对操作同一块区域时,如果不加锁会出现并发问题,数据不是自己预计得到的值。我觉得有点像mysql事务中脏读、不可重复读、幻读的问题。加锁的目的是为了保证同一时间只有我一个人操作同一个资源。

如何在代码里面加锁

jdk提供给了我们很多锁的实现方式,用于各种情况锁的使用:

  1. 使用synchronized修饰方法、修饰代码块等;
  2. 使用ReentrantLock来获取锁;
  3. ReadWriteLock读写分开的读写锁;
  4. ReentrantReadWriteLock;

这些锁有什么区别

  1. 实现原理不同 synchronized是锁实现原理是jdk实现的:
public class SynchronizedDemo {     public static void main(String[] args) {        Object o = new Object();        synchronized (o){            System.out.println("ReentrantLockDemo");        }    }}复制代码

使用synchronized修饰的代码会在编译时加上monitorenter、monitorexit进行修饰,那么问题来了,为什么用这个修饰后就能够保证线程执行过程中的安全呢? 因为jdk在执行monitorenter、monitorexit区块的时候是保证原子性的,要么执行完成要么执行不完成。synchronized修饰的代码块有可视性、原子性、顺序性(防止重排序)。

ReentrantLock是怎么实现锁的机制呢? 通过继承AbstractQueuedLongSynchronizer(AQS)来进行锁的,实现原理是AQS中有一个变量来控制是否获取到了锁,通过Unsafe的CAS操作来获取锁,从而保证线程安全。

那么问题来了?CAS操作的ABA问题如何解决? concurrent包中有提供AtomicStampedReference来解决ABA问题,也就是在CAS操作的同时需要再增加版本的判断,从而保证不出现ABA的问题。

public class SolveCAS {    // 主内存共享变量,初始值为1,版本号为1    private static AtomicStampedReference
atomicStampedReference = new AtomicStampedReference<>(1, 1); public static void main(String[] args) { // t1,期望将1改为10 new Thread(() -> { // 第一次拿到的时间戳 int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+" 第1次时间戳:"+stamp+" 值为:"+atomicStampedReference.getReference()); // 休眠5s,确保t2执行完ABA操作 try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } // t2将时间戳改为了3,cas失败 boolean b = atomicStampedReference.compareAndSet(1, 10, stamp, stamp + 1); System.out.println(Thread.currentThread().getName()+" CAS是否成功:"+b); System.out.println(Thread.currentThread().getName()+" 当前最新时间戳:"+atomicStampedReference.getStamp()+" 最新值为:"+atomicStampedReference.getReference()); },"t1").start(); // t2进行ABA操作 new Thread(() -> { // 第一次拿到的时间戳 int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+" 第1次时间戳:"+stamp+" 值为:"+atomicStampedReference.getReference()); // 休眠,修改前确保t1也拿到同样的副本,初始值为1 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 将副本改为20,再写入,紧接着又改为1,写入,每次提升一个时间戳,中间t1没介入 atomicStampedReference.compareAndSet(1, 20, stamp, stamp + 1); System.out.println(Thread.currentThread().getName()+" 第2次时间戳:"+atomicStampedReference.getStamp()+" 值为:"+atomicStampedReference.getReference()); atomicStampedReference.compareAndSet(20, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName()+" 第3次时间戳:"+atomicStampedReference.getStamp()+" 值为:"+atomicStampedReference.getReference()); },"t2").start(); }}复制代码
  1. 使用场景不同

    ReadWriteLock可以使用在读多写少的情况,尽量提升并发的能力 ReadWriteLock、synchronized使用的是独占锁,但是jdk对synchronized在编译时会有优化。

转载于:https://juejin.im/post/5cc263cff265da038d0b4a53

你可能感兴趣的文章
named启动脚本修改
查看>>
RHEL6.5的安装过程
查看>>
通过vftps和虚拟帐号增强ftp的安全性
查看>>
创客集结号:3D打印的材料
查看>>
Ceph
查看>>
架构的“一小步”,业务的一大步
查看>>
迭代器,生成器
查看>>
如何用二分法在有序数组中找到你想要的数字
查看>>
单向ospf
查看>>
深蓝串口调试工具2017冬季版(2.14.11)
查看>>
linux ssh_config和sshd_config配置文件
查看>>
Oracle教程之管理索引(六)--Oracle重建索引
查看>>
ubuntu编译最简环境
查看>>
回顾一个考务系统的开发
查看>>
何为云计算
查看>>
nginx安装手册
查看>>
【NetApp】snapvault 配置
查看>>
python对象-多态
查看>>
mysql 数据库导入导出方法总结
查看>>
Http协议之防盗链
查看>>