JAVA并发编程常识

avatar 2020年08月26日23:29:02 0 124 views

1 JVM内存模型

堆:

  • 所有对象全部放在共享堆空间中
  • 对象的属性在共享堆空间内
    • 内存单字节对齐,short不变

栈:

  • 每个线程都有独立的线程栈空间
  • 线程栈只存基本类型和对象地址
    • 栈内存4字节对齐,short变int
    • 对象地址4字节,引用堆空间
    • 方法中局部变量在线程栈空间内
  • 方法参数在栈顶交叉,不拷贝
    • 栈顶寄存,减少中间状态读取
    • PC指针记录当前执行位置 

 

2 原子性

  • 对象类型:
    • 对象地址原子读写,线程安全
    • 并发读不可变状态,线程安全
    • 并发读写可变状态,非线程安全
  • 基本类型:
    • int,char数值读写,线程安全
    • long,double高低位,非线程安全
    • i++等组合操作,非线程安全

 

3 可见性

  • final
    • 初始化final字段确保可见性
  • volatile
    • 读写volatile字段确保可见性
  • synchronized
    • 同步块内读写字段确保可见性
  • happen before
    • 遵守happen before次序可见性

 

4 可排序性

Happen Before 法则

  • 程序次序法则
    • 如果A一定在B之前发生,则happen before
  • 监视器法则
    • 对一个监视器的解锁一定发生在后续对同一监视器加锁之前
  • Volatie变量法则
    • 写volatile变量一定发生在后续对它的读之前
  • 线程启动法则
    • Thread.start一定发生在线程中的动作之前
  • 线程终结法则
    • 线程中的任何动作一定发生在括号中的动作之前(其他线程检测到这个线程已经终止,从Thread.join调用成功返回,Thread.isAlive()返回false)
  • 中断法则
    • 一个线程调用另一个线程的interrupt一定发生在另一线程发现中断之前。
  • 终结法则
    • 一个对象的构造函数结束一定发生在对象的finalizer之前
  • 传递性
    • A发生在B之前,B发生在C之前,A一定发生在C之前。 

 

5 系统内存

MESI协议:

  • Modified
    • 本CPU写,则直接写到Cache,不产生总线事务;其它CPU写,则不涉及本CPU的Cache,其它CPU读,则本CPU需要把Cache line中的数据提供给它,而不是让它去读内存。 
  • Exclusive
    • 只有本CPU有该内存的Cache,而且和内存一致。 本CPU的写操作会导致转到Modified状态。 
  • Shared
    • 多个CPU都对该内存有Cache,而且内容一致。任何一个CPU写自己的这个Cache都必须通知其它的CPU。 
  • Invalid
    • 一旦Cache line进入这个状态,CPU读数据就必须发出总线事务,从内存读。 

 

6 内存栅栏

  • 读:
    • volatile int a, b; if(a == 1 && b == 2)
    • JIT通过load acquire依赖保证读顺序:
      • 0x2000000001de819c:  adds r37=597,r36;;  ;...84112554
      • 0x2000000001de81a0:  ld1.acq r38=[r37];;  ;...0b30014a a010
  • 写:
    • volatile A a; a = new A();
    • JIT通过lock addl使CPU的cache line失效:
      • 0x01a3de1d: movb $0x0,0x1104800(%esi);
      • 0x01a3de24: lock addl $0x0,(%esp);

 

7 查看JIT编译结果

java -XX:+UnlockDiagnosticVMOptions -XX:PrintAssemblyOptions=hsdis-print-bytes -XX:CompileCommand=print,*AtomicInteger.incrementAndGet

 

8 对齐

LinkedTransferQueue
static final class PaddedAtomicReference <T> extends AtomicReference <T> {
    Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe;
    PaddedAtomicReference(T r) {
        super(r);
    }
}

16个地址的长度,刚好占满一个cache line的长度。

确保两个引用,不在同一cache line上,防止多锁竞争。

 

9 引用

private Channel channel;

public void setChannel (Channel channel ) {
    this.channel  = channel;
}

public void run() {
	Channel channel = this.channel; // localed reference
    if (channel != null && channel.isConnected()) {
        // do something …
    }

}


public void check() {
    if (channel != channel)
        throw new Error("check error!");
}

 

10 单例

 

 

11 多锁

 

12 计数

 

 

13 缓存

 

 

14 线程安全策略

  • 不可变类
    • 如果一个类初始化后,所有属性和类都是final不可变的,则它是线程安全,不需要任何同步,活性高。
  • 线程栈内使用
    • 方法内局部变量使用
    • 线程内参数传递
    • ThreadLocal持有
  • 同步锁
    • synchronized的代码串行执行,线程安全,但活性低。
    • volatile变量锁外双重检测(JDK1.5+),降低锁竞争。
    • 读写条件分离,锁粒度分级,排序锁。
  • CAS (CompreAndSet)
    • 循环设新值,如果旧值变化,则重设,乐观并发。

 

15 习惯

敲每个点号时,考虑:

  • 会不会出现空指针?
  • 有没有异常抛出?
  • 是不是在热点区域?
  • 在哪个线程执行?
  • 有没有并发锁间隙?
  • 会不会并发修改不可见?

 

 

转自梁飞的PPT

下载地址:链接:https://pan.baidu.com/s/1qf7tidbw-eDKm1w35BfMCQ  密码:b43z

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
您需要登录才能评论,可以选择注册或者QQ快速登录

     

已通过评论:0   待审核评论数:0