synchronized锁
synchronized锁
对象锁
- 一个对象一把锁
类锁
判断是同步还是异步关键看是不是同一把锁
- 锁加在方法上或在方法中写对象是个对象锁
- 锁加在类上或静态方法上是类锁
synchronized锁的底层实现原理
jvm是基于进入和退出Monitor对象来实现方法同步和代码块同步
代码块的同步
-
代码块的同步是利用monitorenter和monitorexit这两个字节码指令
- 分别位于同步代码块的开始和结束位置
当jvm执行到monitorenter指令的时候,试图获取monitor对象的所有权,获取成功锁的计数器+1,当执行到monitorexit的时候,锁计数器-1,当锁计数器=0,锁就被释放,如果获取monitor对象失败,就会进入阻塞的状态
方法级的同步为隐式
- 不是通过字节码指令来控制,它实现在方法调用和返回之中
- JVM可以从方法常量池的方法表结构(method_info Structure)的ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法
- 当方法调用时,调用指令检查ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,该线程将先持有monitor,然后执行方法,最终方法执行完(无论成功、失败)释放monitor
synchronized锁升级过程
1、无锁状态
2、偏向锁
-
适合只有一个线程执行同步代码块
- 当有另外一个线程进来立即转成轻量级锁
3、轻量级锁乐观锁
- 多个线程竞争偏向锁,偏向锁关闭,升级成重量级锁,中间过程有个锁自旋(1.6引入自适应锁自旋,会根据锁上一次的自旋时间来判断自旋的次数)
4、重量级锁
synchronized锁优化
减少synchronized的范围,同步代码块中的代码尽量少
降低synchronized锁的粒度
- 尽量不使用同一个锁
分段锁
- concurrentHashMap:局部锁定
- hashtable:增删该全部上锁
读写分离,读取时不加锁,写入和删除的时候上锁
锁消除
-
去除那些不可能发生资源竞争的锁
- 比如说一些线程安全的集合,默认加了锁,JVM根据逃逸分析进行锁消除
锁粗化
-
将多个连续的加锁、解锁连在一起,扩成一个更大范围的锁
- for循环,加锁放在循环外面
1.6引入的自适应锁自旋
- 根据锁上一次的自旋时间来判断
CAS
定义:在并发量不是很高的情况下,先查一次,然后进行修改,在真正的写数据时,会再查一次,比较两次查询的结果,如果不一样说明是不安全的,如果一样说明安全,会进行修改
在高并发的情况下,会有一个忙循环的过程,消耗cpu
ABA问题
- 通过维护一个版本号来解决
- 之前读和过段时间读,可能中间会被第三个人修改过,但是又被改回来
CAS只能针对一个共享变量
ReentrantLock与synchronized的区别
1、从jvm角度来看synchronized其实jvm的一个关键字,ReentrantLock是一个类
2、synchronized不需要关心锁的释放,ReentrantLock需要手动lock利用try..catch..finanlly释放锁
所有线程能够看到共享内存的最新状态
volatile关键字
保证内存的可见性
防止指令重排
- 防止Jvm继续指令重拍优化
- PS:单线程下指令重排会考虑数据的依赖性,不会影响执行结果
2、多线程会有影响
不保证原子性
-
使用并发包下的Atomic的类
- 如AtomicInteger.....
PS:底层通过屏障指令保证可见性、有序性,刷新主从的数据
ps:锁是不可逆的,一旦升级成重量级的锁就不变了
lock与synchronized的区别
lock需要手动加锁、释放锁
synchronized在发生异常,会自动释放线程占有锁,不会发生死锁,lock必须将unLock()放到finally{} 中
lock支持实现公平锁(按照加锁的顺序,先来的先拿到锁)和非公平锁,synchronized只支持非公平锁
死锁产生的四个必要条件
互斥条件
- 在某段时间某个资源被一个线程占用,另外一个线程请求资源,只能等待
请求和保持
- 进程已至少保持一个资源,又提出了新的资源请求,而其他资源又被占用,此时请求线程阻塞,自己已获取的资源又不能释放
不剥夺条件
- 自己已获取的资源,在未使用完之前,不能被剥夺,只有使用完才可释放
环路等待条件
- 发生死锁时,存在进程---资源的环形链
案例
- Test01.java
synchronized保证内存可见性
AQS
定义:队列同步器
两种资源共享方式
-
Exclusive
- 独占,只有一个线程能执行,如ReentrantLock
-
Share
- 共享,多个线程可同时执行,如Semaphore/CountDownLatch
本质使用CLH的同步队列存放线程资源
-
先进先出
入列
出列
锁的基本概念
互斥锁
- 每个对象都对应一个可称为“互斥锁”的标记,保证在任意时刻,只有一个线程可以访问该资源
阻塞锁
- 让线程进入阻塞状态
自旋锁
读写锁
公平锁
- 排队,先来先得
非公平锁
- 不考虑排队问题
ReentrantLock
相比于synchronized性能会好些
互斥锁
- 同一时间仅有一个线程可以访问,所以就有了ReentrantReadWriteLock读写锁
ReentrantReadWriteLock
- 读写锁ReentrantReadWriteLock实现接口ReadWriteLock,该接口维护了一对相关的锁,一个用于只
读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是
独占的
锁降级
- 获取写锁-》获取读锁-》释放写锁
JUC
CyclicBarrier
- 同步屏障
Semaphore
- 通过已有的信号量,对资源的线程访问数量进行控制,有公平和非公平两种实现方式
CountDownLatch
- 计数器没到,继续等待,到了0就可以执行了
- PS:计数无法被重置