并发编程笔记1

1、并发编程挑战

1.1、上下文切换:

  1.1.1、什么是上下文切换?

    单核也支持多线程执行,原因是通过时间片轮换,时间片是CPU分配给各线程的时间,在切换过程中,会先保存上个任务信息,再加载当前任务信息,所以任务从保存到再加载的过程就是一次上下文切换。

  1.1.2、并行vs串行

    并发执行速度比串行慢的原因是:线程创建以及上下文切换的开销。

  1.1.3、查看上下文切换

    使用vmstat cs(content switch)

  1.1.4、如何减少上下文切换

    • 无锁并发编程。例如:将数据IDHash算法取模分段,特定线程处理特定段数据。
    • CAS算法。例如 Atomic包使用CAS算法更新数据,不需要加锁
    • 使用最少线程。  
    • l协程:在单线程里实现多任务调度。

  1.1.5、减少上下文切换实战

    可以通过减少线上大量WAITING的线程,减少上下文切换次数

1.2、死锁

  产生死锁的原因:线程t1和线程t2互相持有并等待对方释放锁。

  发生死锁查看步骤:不能提供服务,dump线程查看哪个线程出现问题

  避免死锁方法:

    1、避免一个线程同时获取多个锁;

    2、避免一个线程在锁内占用多个资源,尽量保证只占每个锁占用一个资源。

    3、使用定时锁,如lock.tryLock(timeout)替代使用内部锁机制。

    4、对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

1.3、资源限制的挑战

  (1) 什么是资源限制?

    硬件资源:带宽上传/下载速度,硬盘读写速度,CPU处理速度,内存

    软件资源:数据库连接,socket连接数等

  (2) 资源限制引发问题

    并发编程速度快原因:将代码中串行执行部分变成并发执行;若并发执行,由于资源限制就会串行化执行,就会增加上下文切换和资源调度的时间消耗。

  (3) 如何解决资源限制

    采用集群并行执行,例如hadoop,不同的机器处理不同的数据,“数据ID%机器数”,计算得到一个机器编号,然后由对应编号的机器处理这笔数据。

  (4) 资源限制下的并发编程

    根据不同的资源限制调整程序的并发度

    建议使用JDK并发包提供并发容器和工具类来解决并发问题

2、Java并发机制的底层实现原理

2.1Volatile

  2.1.1volatile的应用

  ① 保证共享变量“可见性”,可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改值;

  ② 轻量级synchronized,执行成本更低,不会引起线程上下文切换和调度;

  ③ 避免指令重排;

  ④ 8字节(long,double),一次性赋值;

2.1.2Volatile修饰变量,JIT编译生成汇编语言会加Lock前缀,从而引发两件事情:

  1)、将当前CPU缓存行的数据(工作内存)写回到系统内存(主存)

  2)、这个写回内存的操作会使其他CPU缓存该内存地址数据失效。

  缓存一致性:在多处理器下,每个处理器通过嗅探总线的数据来检查自己缓存的值是否过期,当发现自己缓存行对应的内存地址被修改,会将自己的缓存行数据设置无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中将数据重新读取。

Volatile使用优化:LinkedTransferQueue

2.2synchronized的实现原理和应用

  2.2.1synchronized基础

  ① Java中每一个对象都可以作为锁;

  ② 锁普通方法,锁是实例对象;

  ③ 锁静态方法,锁是当前类Class对象;

  ④ 锁同步代码块,锁是配置对象。

JVM基本原理:使用Monitor对象,代码块同步是使用monitorentermonitorexit指令实现。

  2.2.2Java对象头

    对象头长度:Mark Word【锁状态,对象的hashCode,对象分代年龄,是否是偏向锁,锁标志位】

2.2.3、锁的升级与对比

锁状态:

无锁状态,偏向锁,轻量级锁,重量级锁

优点

缺点

适用场景

偏向锁

加锁和解锁不需要额外的消耗,和执行同步非同步方法性能差不多

如果线程间存在锁竞争,会带来额外锁撤销的消耗

适用于只有一个线程访问同步块场景

轻量级锁

竞争的线程不会阻塞,提高了响应速度

如果始终得不到锁竞争的线程,使用自旋会消耗cpu

追求响应时间

同步块执行速度非常快

重量级锁

线程竞争不使用自旋,不会消耗cpu

线程阻塞,响应时间缓慢

追求吞吐量,同步块执行速度较长

2.3 原子操作的实现原理

  原子操作:不可被中断的一个或一系列操作。

  CAS(Compare and Swap):CAS操作需要输入两个数值,一个旧值(期望操作前的值),和一个新值,在操作期间先比较旧值有没有发生变化,如果没有发生变化,才交换心志,发生了变化则不交换。

2.3.1、处理器如何实现原子操作:

  (1) 使用总线锁保证原子性;

  (2) 使用缓存锁保证原子性;

2.3.2Java如何实现原子操作

  ① 使用循环CAS实现原子操作,如Java并发包中Atomic提供

  ② CAS实现原子操作问题

    1) ABA问题,解决方法:AtomicStampedReference就是每个更新把版本号加1

    2) 循环时间长开销大;

    3) 值能保证一个共享变量的原子操作,(取巧方法,把多个共享变量合并成一个共享变量来操作,如i=2,j=a,合并为ij=2a

原文地址:https://www.cnblogs.com/atomicbomb/p/10083417.html