java并发编程的艺术1

avatar
作者
猴君
阅读量:0

1.上下文交换

减少上下文交换的方法

  • 无锁并发编程:进行数据操作,多线程竞争锁,引起上下文切换。将变量按照id 进行hash,不同线程处理不同段的数据

  • CAS(compare and swap)

    A线程和B线程都要修改变量X,先从内存中读取X放在各自的缓冲区。然后修改,在写回之前,先对比缓存区的和内存中的数据一样吗,一样再写回。

  • 创建线程数少一点

2.死锁

充要条件:互斥、请求保持、循环等待、不可剥夺

避免死锁:避免一个线程同时获得多个锁、避免一个锁同时获得多个资源、定时锁、加锁和解锁必须在一个数据库连接中

3.并发机制的底层实现原理

(1)voliate(保障数据修改的可见性)

step1:任意线程对数据的写入立刻刷新到主内存

使用汇编命令的LOCK#信号,确保在声言该信号期间,处理器可以独占任何共享内存。锁总线或者锁缓存,一般锁缓存。为了解决锁总线的劣势,引入了MESI协议。

step2:让缓存区的数据失效。

处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。不一致缓存中的数据就失效。

(2)synchronized

Java中的每一个对象都可以作为锁,对象头中有标记位标记是否锁。

  • 对于普通同步方法,锁是当前实例对象。

  • 对于静态同步方法,锁是当前类的Class对象。

  • 对于同步方法块{},锁是Synchonized括号里配置的对象。

代码块同步使用Monitorenter和monitorexit实现。monitorenter放在方法开始的时候,monitorexit放在方法结束和异常的地方。任何一个对象都有一个monitor与之关联。

(3)synchronized用的锁是存在Java对象头里的。

markword:存放hashcode、分代年龄、是否是偏向锁、锁类型标记位。

类型指针:

数组长度:

(4)锁的升级和对比

无锁状态、偏向锁状态、轻量级锁、重量级锁(只能升级不能降低)

**偏向锁加锁:**访问同步代码块并且获取锁的时候,就在对象头和栈帧的锁记录里面存储偏向的线程id。原来对象头存的是hashcode和分代年龄=》(变为)线程id。

在这里插入图片描述

释放偏向锁:等待竞争才会释放锁。等待全局安全点,看看持有偏向锁的线程还活着嘛。如果挂了就改成无锁状态,然后偏向本线程。如果线程还活着,就遍历栈帧和对象,锁记录偏向其他线程或者恢复成无锁状态、或者标记对象不适合使用偏向锁(锁升级)。

出现竞争、偏向次数太多了(锁升级)。

**轻量级锁加锁:**在线程栈创建锁空间,复制堆中对象Markword的数据,到锁空间中。多个线程通过CAS将堆中对象头的Mark word改成锁空间的地址。成功的那个线程就抢到锁了。加锁失败的线程一直自旋。阈值10升级成重量级锁。(while(true))

轻量级锁释放锁:使用原子的CAS操作将Displaced Mark Word替换回到对象头。如果失败了就是有线程在竞争锁,重新执行代码块的内容。

在这里插入图片描述

(5)CAS实现原子操作的三大问题

  • ABA问题:A->B->A,CAS算法认为它没有变。解决思路就是使用版本号。
  • 循环时间长开销大
  • 只能保证一个共享变量的原子操作

4.JMM java内存模型

在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!