# concurrencystudy **Repository Path**: kenwar/concurrencystudy ## Basic Information - **Project Name**: concurrencystudy - **Description**: 并发学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-05 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 多线程并发学习笔记 ## 为什么并发程序容易出问题? CPU、内存、I/O设备三者速度差异,CPU>内存>I/O,根据木桶理论(一只木桶能装多少水,取决于它最短的那块木板),程序整体性能取决于最慢的操作——读写I/O设备,单方面提高CPU性能是无效的。 大多数时间CPU都因为等待I/O处于空闲状态,为了合理利用CPU的性能,平衡三者速度差异: - CPU增加了高速缓存,以均衡与内存的速度差异(带来了**可见性**问题VisibilityDemo.class) - 操作系统增加了进程、线程,以分时复用CPU(分时复用CPU带来了**原子性**问题) ,均衡CPU与I/O设备的速度差异(进程OR线程在读写I/O设备时将释放CPU资源,让CPU在这段时间内可以执行其他线程的任务,I/O设备在一次读写完后 ,如果发现还有排队的读写任务,就可以马上进行下一次读写,充分利用CPU与I/O设备的性能) - 编译程序优化指令执行次序,使得缓存能够得到更合理的利用(存疑:如何优化?为什么更合理,举例)(编译优化带来了**有序性**的问题:双重校验实现单例模式时 ,因为编译程序会将赋值操作由:1. 申请内存空间 2. 构造对象 3. 引用指向内存地址。 优化为:1.申请内存空间 2. 引用指向内存地址 3. 构造对象 演示:DoubleCheckSingleton.class) ## Java如何解决可见性和有序性问题 解决缓存带来的可见性问题最简单的办法就是禁用缓存,解决编译优化带来的有序性问题最简单的办法就是禁用编译优化 ,但为了性能,我们应该要**按需禁用缓存和编译优化**。 Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。包括:**volatile**、**synchronized**、**final**三个关键字以及六项**Happens-Before**原则。 ### Happens-Before 原则 - 程序有序性原则:在一个线程中前一个操作Happens-Before后一个操作 - volatile变量原则:对volatile变量的写操作Happens-Before于对该变量的读操作 - 传递性原则:如果A Happens-Before B,且B Happens-Before C,那么 A Happens-Before C - 管程锁原则:一个unlock操作Happens-Before与后面对同一个锁的lock操作(必须是同一个锁) - 线程start()规则:主线程A执行B.start(),主线程A调用start之前的操作Happens-Before与B线程run中的所有操作 - 线程join()规则:主线程A等待子线程B完成(指主线程A通过调用子线程B的join()方法实现),子线程B的run中的操作Happens-Before与主线程调用该join操作的返回 - 线程中断规则:对线程发起的interrupt()事件Happens-Before于被中断线程中的代码检测到中断事件的发生 - 对象终结规则:一个对象初始化的完成(构造函数执行结束)Happens-Before于它的finalize()方法的开始 ## 互斥锁