一、锁是什么

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

二、如何在代码里面加锁

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

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

三、这些锁有什么区别

Ⅰ、实现原理不同

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,版本号为1private static AtomicStampedReference<Integer> atomicStampedReference = newAtomicStampedReference<>(1, 1);public static void main(String[] args) {// t1,期望将1改为10new 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也拿到同样的副本,初始值为1try { 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();}
}

Ⅱ、使用场景不同

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

更多关于Java的技术和资讯可以关注我的专栏:

Java架构筑基​zhuanlan.zhihu.com

专栏免费给大家分享Java架构的学习资料和视频

java并发排它锁_Java并发编程进阶——锁(解析)相关推荐

  1. java 并发队列_JAVA并发编程:阻塞队列BlockingQueue之SynchronousQueue

    前面在讲解Executors工厂创建可缓存线程的线程池(newCachedThreadPool)的时候有提到过SynchronousQueue队列,该线程池使用 SynchronousQueue 作为 ...

  2. java并发常量_Java并发编程-常量对象(七)

    在创建后状态不再发生改变的对象称作常量对象(Immutable Objects).常量对象其可靠性使其广泛地用作开发简单可靠代码的策略.常量对象在开发并发程序中非常有用.由于创建后不能被改变状态,它们 ...

  3. java并发调用_Java并发教程–可调用,将来

    java并发调用 从Java的第一个发行版开始,Java的美丽之处之一就是我们可以轻松编写多线程程序并将异步处理引入我们的设计中. Thread类和Runnable接口与Java的内存管理模型结合在一 ...

  4. java并发队列_Java并发教程–阻塞队列

    java并发队列 如第3部分所述,Java 1.5中引入的线程池提供了核心支持,该支持很快成为许多Java开发人员的最爱. 在内部,这些实现巧妙地利用了Java 1.5中引入的另一种并发功能-阻塞队列 ...

  5. java线程死锁_Java并发:隐藏线程死锁

    java线程死锁 大多数Java程序员熟悉Java线程死锁概念. 它本质上涉及2个线程,它们彼此永远等待. 这种情况通常是平面(同步)或ReentrantLock(读或写)锁排序问题的结果. Foun ...

  6. java lock 对象_Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  7. java计算时间差_JAVA并发编程三大Bug源头(可见性、原子性、有序性),彻底弄懂...

    原创声明:本文转载自公众号[胖滚猪学编程]​ 某日,胖滚猪写的代码导致了一个生产bug,奋战到凌晨三点依旧没有解决问题.胖滚熊一看,只用了一个volatile就解决了.并告知胖滚猪,这是并发编程导致的 ...

  8. java 延迟初始化_Java并发编程——延迟初始化占位类模式

    --仅作笔记使用,内容多摘自<java并发编程实战> 在并发编程中,如果状态变量仅在单个线程中初始化和使用,自然是线程安全的,但一旦涉及到线程间的数据交互,如何声明一个用于多线程的单例状态 ...

  9. java社区活跃度_Java并发编程-活跃度问题

    在讲问题前,我先说明一下什么是活跃度? 一个并发应用及时执行的能力称作活跃度. 我主要讲死锁问题,顺带介绍一下饥饿,弱响应性和活锁. 死锁 死锁这个词大家都听过,我先来罗列一下产生死锁的四个必要条件: ...

最新文章

  1. 字符串编辑距离(Edit Distance)
  2. LeetCode简单题之验证外星语词典
  3. Science:组合图表绘制
  4. blockchain 区块链200行代码:在JavaScript实现的一个简单的例子
  5. poj 3522 Slim Span
  6. python关于二手房的课程论文_python之数据清理-以二手房信息为例
  7. C语言之浅析网络包解析
  8. Android 内存映射mmap浅谈
  9. mysql启动的errmsg.sys文件路径问题
  10. 提示Algorithm negotiation failed 错误该如何解决
  11. 形状的度量Measures of Shape
  12. 大学本科计算机专业那些课 左飞
  13. 我为什么反对用各类框架
  14. 【算法面试】leetcode最常见的150道前端面试题 --- 中等题
  15. 求解答:UKF对路面附着系数估计报错
  16. 语音转文字转换器市场现状研究分析-
  17. maven 创建java项目_手把手教你创建Java Maven依赖项目
  18. 原理c语言for循环延时1s,for循环实现C语言精确延时
  19. ERR_SPDY_PROTOCOL_ERROR错误解决
  20. 百度与中国宝武达成战略合作 打造“AI+钢铁”示范样本

热门文章

  1. python中什么是实例-在Python中使用’__rsub__’方法的典型实例是什么?
  2. php mysql 批量insert_mysql批量插入数据方法
  3. foreach、qAsConst用法总结
  4. QFontDatabase: Cannot find font directory
  5. linux 实时功能,linux – 什么是同时具有实时循环和非实时功能的更好方法
  6. make: *** 没有规则可以创建“default”需要的目标“build”
  7. iOS 7开源项目干货集合
  8. 置顶带滚动效果_前端面试:如何实现轮播图效果?
  9. Udp广播的发送和接收(iOS + AsyncUdpSocket)下篇
  10. 如何在51cto博客中添加QQ链接