JUC 并发编程_锁
synchronized
关键字
同步锁
修饰代码块和方法
修饰方法不能被继承
修饰静态方法 相当于锁住了整个类
修饰代码块
同一个时间只能有一个人操作这个代码块
售票
出现抢票
没抢到一直抢
一直抢到为止
只有一个线程能通过
不存在公平
排他锁
隐式可重入锁
同一个线程对对象反复加锁
必须是同一个线程
隐式 : 看不到第二把锁 也控制不住
对对象反复加锁
成功可重入
失败 不可重复
堆
对象头
-
对象
- 对象头
- 实例数据
- 对其填充
-
对象头
- markword : 存储对象的哈希码 GC分代的年纪 锁信息
- 一个类的元数据地址
- 数组长度 有数组就有着玩意 没有那就没有 专门存数组的
-
markword 32
- HashCode 25 GC4 锁信息3
- 锁状态
- 无锁 一个对象没有竞争 效率最高
- 偏向锁 比如被 synchronized修饰 没有竞争
- 把名字写到线程ID中 偏向锁从0变1
- 如果下次不用的话只是把线程ID清除
- 偏向锁 有竞争
- 双方都争抢着把自己的名字写到线程ID中去
- 但是一方是带着synchronized来的所以另一方一直失败
- 一直到清掉的一瞬间 另一方抢到锁
- 抢到了锁 加ID 不加偏向锁 因为他知道有人抢 不保险
- 升级轻量级 锁标识位变成00
- 操作完清ID 后面没有人操作 时间到 jvm直接清锁
- 重量级 锁标识位11 竞争激烈
- 一个对象无锁 发现synchronized 加偏向锁
- 然后一堆人操作这个对象
- 其中一个人操作到了 加轻量级 写ID
- 剩下一堆人中的一个人操作到了这个对象直接加重量级锁
- 锁状态
- 1.8以前直接上重量级
- HashCode 25 GC4 锁信息3
锁消除
编译时 如果对象每次都是自己new 出来的 没有竞争 那么运行时程序会把锁消除
锁粗化
编译时 加锁 加一万次 和一次没区别的化 那么编译器会直接优化成一次
自旋锁和自适应自旋锁
- 自旋
- 枪锁的时候会写自己的ID
- 写完ID改锁标识位
- 这个改ID的过程就是叫做自旋
- 死循环写ID
- 里面枪锁的过程有个while(true){}循环
- 抢到了 跳出break
- 循环执行几次 就是自旋了几次
- 区别
- 告诉下一个枪锁的 别的线程循环抢到锁的次数作为参照值
- 然后大于等于上一次的次数 参照值 超过就不强了
Lock
public interface Lock {
void lock();//加锁 成功失败 结果不知道 就试一次
void lockInterruptibly() throws java.lang.InterruptedException; //中断锁
boolean tryLock();//尝试加锁 知道结果
boolean tryLock(long l, java.util.concurrent.TimeUnit timeUnit) throws java.lang.InterruptedException;//重载方法没有 参数 给时间段 直到为true或者时间到下一步
void unlock();//释放锁
java.util.concurrent.locks.Condition newCondition();//钥匙
}
加锁一次 成功就成功 不成功就阻塞
如果Lock不释放锁就会死锁
集合线程安全
ArrayList 多个线程同时堆集合进行修改
如果有同步操作 就会出现并发修改异常
如何解决List类型的线程安全问题?
用Vector
他基于AbstactList是实现List
add方法被synchronized同步修饰 所以没有并发修改异常
用Collections
里面也提供了synchronized方法 保证list同步线程安全
CopyOnWriteArrayList
和ArratList一样是一个可变数组
线程安全
往容器中添加数据的时候 不直接添加 先复制容器 然后在新的容器里面添加然后再将原容器指向新容器
会产生数据不一致 会产生脏数据