Java多线程

并发编程艺术读书笔记

并发机制底层原理

volatile原理

  • 可见性
    当某个处理器更新完这个变量后,其他变量也会收到通知; 是通过lock前缀指令来实现;

lock前缀指令会:
1.将当前处理器缓存行数据刷新到内存;
2.写回内存操作会使得其他处理器缓存的该变量无效;

第二点是通过缓存一致性协议保证的。

缓存一致性协议:每个处理器嗅探总线上数据,如果发现自己缓存的数据过期后,则会把自己缓存的设为无效状态,会重新从内存里获取一份;

  • 禁止指令重排序
    是通过插入内存屏障来实现的。

volatile不能保证原子性,能保证可见性;

synchronized 原理

  • 代码块加synchronized
    会生成monitorenter 和monitorexit指令
  • 方法加synchronized
    加一个标记 TODO

当一个monitor被一个线程持有后,会出于锁定状态,其他线程无法获取该锁,除非释放掉;

锁是放在对象头的mark word 里;mark word包含分代年龄、hashcode、偏向标记、锁标记;

优化后的synchronized 锁一共有四种状态:
无锁、偏向锁、轻量级锁、重量级锁,级别从低到高;能升级但不能降级;

这几种锁的优缺点及场景:
偏向锁:当同一线程再次获取锁时,设置一下标记,适用于只有一个线程的场景,加锁和解锁代价低;

轻量级锁:使用cas操作将线程的锁记录替换掉mark word ,成功则获取锁,失败则自旋cas 尝试,注意不是阻塞,自旋能提高线程响应速度,适用于同步代码块执行速度很快的场景;

重量级锁:线程竞争会阻塞,阻塞不会消耗cpu,加锁和解锁代价大;

cas

cas:compare and exchange 一个expect旧值和一个update值,如果expect旧值没变化,则更新为新值;cas是通过cmpxchg指令来实现的。

aba 问题:针对cas aba问题,可以加版本号,每操作一次值版本号增加;
cas 是自旋循环尝试,消耗cpu

Java内存模型

  • JMM:
    内存模型 java memory model :主内存,线程可以共享主内存的东西;私有内存,线程独享私有区域;

  • 重排序
    为了提高性能对指令序列进行重新排序,编译器和处理器都可能进行重排序
    as-if-serial 原则:不管如何重排序,单线程的结果不会变

如何禁止重排序?通过插入特定的内存屏障;

  • happens-before规则:
    程序顺序执行规则:一个线程的操作,先于后续其他操作,按照代码顺序执行;
    监视器锁规则:解锁先于随后对这个锁进行加锁;
    volatile规则:volatile的写先于后续对volatile的读;
    start规则:A线程启动B线程,在start前A的所有操作先于B的操作;
    join规则:线程里操作代码先于检测线程存活代码Thread.join,Thread.isAlive;
    Interupt规则:对线程中断interupt先于后续线程检测中断;

参考happens-before

  • final内存语义
    final 修饰的变量只能赋值一次

  • final 写重排序规则
    在构造函数中,对final域变量赋值,与随后把包含final域的构造的对象赋值给其他引用,这两个操作之间不能重排序,即编译器不能把final域变量重排序到构造函数的return 语句之后,是通过在return 语句和final 赋值之间插入StoreStore内存屏障来实现的。这保证了初始化final域对其他线程可见;

  • final 读重排序规则
    初次读包含final域的对象引用与读final 域 ,这两个操作不能重排序,因为存在依赖关系,通过插入loadload屏障;

多线程

Lock锁和Condition条件
Lock是jdk层面实现,lock更free,可以搭配多个condition;

CountDownLatch、CyclicBarrier和 Semaphore
CountDownLatch:一个线程等待多个线程的执行到某个合适点后再执行;如接力跑,a,b,c三人接力跑,a开跑的条件为拿到b,c的接力棒,否则就等待;
CyclicBarrier:同步一组线程,如多人跑步,有的跑得快,有的跑得慢,跑完一圈后都停下来,直到所有的人都跑了一圈,才开始进行下一圈;
Semaphore:控制并发执行数量,如僧人很多,喝粥的碗少,只有等上一个僧人喝完粥把碗空出来之后下一个人才能喝;

interrupt、interrupted和isInterrupted的区别
interupt和isInterrpted是Thread的方法,用于设置中断状态以及查询中断状态,interrupted是
Thread的静态方法,用于查询当前线程的中断状态,带清除功能;
线程因wait,sleep,join等待时,调用interrupt,会收到InterruptedException异常,同时中断标记被清除;
join:线程的非静态方法,当在A线程中调用线程对象B的join方法,调用后,A线程处于等待状态,直到B线程运行结束,也可以在join的时候设置时间;

JVM内存结构 VS Java内存模型 VS Java对象模型
JVM内存结构,和Java虚拟机的运行时区域有关,指的是堆、栈、方法区、常量池等;
Java内存模型,和Java的并发编程有关,每个线程都有自己私有的区域;
Java对象模型,和Java对象在虚拟机中的表现形式有关,对象头;

你可能感兴趣的:(Java多线程)