跨出边界
发布于 2023-12-03 / 48 阅读 / 0 评论 / 0 点赞

多线程实战笔记-java同步机制


内部锁synchronized

java平台中的任何一个对象都有唯一一个与之关联的锁,这种锁称为监视器(Monitor)

  1. 同步方法

  2. 同步静态方法-以当前类对象为引导锁的同步块

  3. 同步实例方法-以this为引导的同步块

  4. 同步方法块

  5. 引导锁

  6. 该锁引导的同步块

锁句柄的变量通常使用private final修饰,如果不用final修饰,那么该值一旦改变,会导致执行同一个

显示锁Lock

显示锁调度

ReentrantLock支持公平和非公平策略,默认非公平。公平策略适用于锁被持有的时间相对较长或者线程申请锁的间隔相对较长。

与内部锁的比较

  1. 灵活但易错

  2. trylock

  3. 支持公平和非公平

  4. 提供接口进行锁信息监控

读写锁

ReentrantReadWriteLock锁降级但不可升级

适用场景

  • check-then-act

  • read-modify-write

  • 多个线程对多个共享数据更新
    例如配置更新

内存屏蔽

内存屏蔽是被插入到两个指令之间进行适用的,禁止编译器和处理器重排序

锁与重排序

volatile

作用

保障可见性,有序性和(long/double)读写操作的原子性

开销

  • 读写都不会导致上下文切换,

  • 写volatile对象会使该操作以及该操作之前的的任何写操作对其他处理器同步

  • 读操作都需要从高速缓存或者主内存中读取数据,成本比普通高,比临界区低

典型场景

  1. 作为状态标志

  2. 保障可见性

  3. 特定场景下替代锁
    将一组变量封装成对象,用volatile申明,修改时构建新对象然后进行赋值,保障可见性和原子性

  4. 简易版读写锁

单例

  1. 单线程

  2. 简单加锁

  3. 双重锁

  4. 双重锁+volatile

  5. 静态内部类
    类的静态变量被初次访问时会触发Java虚拟机对该类进行初始化

  6. 枚举
    枚举实例在初次访问时才会进行初始化,访问枚举类本身(Singleton.class.getName())并不会导致实例初始化

CAS与原子变量

cas将read-modify-write和check-and-act等操作转换成原子操作

bool compareAndSet(Object now,Object oldObj,Object newObj){
    if(oldObj==now.get()){
       //没被其他线程改过
       now.set(newObj);
       return true;
    }
    return false;
}
  • cas只保证原子性,可见性依旧由volatile保证

  • 原子变量类Atomic

  • 共享变量的值经过A-》B-》A操作,ABA问题是否可接受与变量值的算法有关,可通过AtomicStampedReference类实现带版本号的变量修改

对象的发布与逸出

对象发布

  1. 使用共享privaate

  2. 存储到public对象中,例如public Map

  3. 使用非private方法返回对象

  4. 创建内部类

    使用外层类名.this访问外部类对象
    
  5. 通过方法调用将对象传递给外部方法,说的是通过继承覆盖可覆盖方法,传递对象

对象初始化

  1. Java类初始化采用延迟加载技,类被虚拟机加载之后所有的静态变量都是默认值,直到有线程初次访问该类的任意一个静态变量时,才执行static方法块

  2. 由于重排序,一个线程读取到一个对象引用时,该对象可能还未初始化完毕,final保证当一个对象被发布到其他线程时,取到的一定是初始值而非默认值


评论