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以前直接上重量级

锁消除

编译时 如果对象每次都是自己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一样是一个可变数组

线程安全

往容器中添加数据的时候 不直接添加 先复制容器 然后在新的容器里面添加然后再将原容器指向新容器

会产生数据不一致 会产生脏数据

你可能感兴趣的:(JUC,jvm,java,开发语言)