• 1. java内存模型与并发技术yangjs@alibaba-inc.com
  • 2. 主要内容学习java并发理论基础:Java Memory Model 学习java并发技术基础:理解同步是如何工作 分析程序什么时候需要同步 几个典型的并发设计策略
  • 3. 一、Java Memory Model内存模型概念 Java Memory Model理论基础 2.1. Ordering&visibility 2.2. Happens-Before Memory Model 2.3. The rules for happens-before 2.4. Java Memory Model相关术语 2.5. JVM原子行为定义 2.6. 案例分析
  • 4. 1、内存模型概念缓存一致性模型 顺序一致性模型:要求对某处理机所写的值立即进行传播,在确保该值以被所有处理机接受后才能继续其他指令的执行 释放一致性模型:允许将某处理机所写的值延迟到释放锁时进行传播
  • 5. 1、内存模型概念内存模型 Describes execution trace of a program Describes possible behaviors of a program Determines what values can be read at every point in the program
  • 6. 1、内存模型概念内存模型的特征 Visibility 可见性 (多核,多线程间数据的共享) Ordering 有序性 (对内存进行的操作应该是有序的) ----接下来我们看看Java内存模型(JMM)
  • 7. 2、Java Memory Model理论基础定义了Java线程和内存交互的规则 保证多线程程序结果的可预测,语义一致 性 Heap Memory: 用来在线程之间共享内存 instance fields, static fields array elements 线程本地变量(局部变量,方法参数等)在堆栈中,不受JMM影响----JMM相关术语
  • 8. 2、Java Memory Model理论基础JMM怎么体现 可视性(Visibility) ? 在JMM中, 通过并发线程修改变量值, 必须将线程变量同步回主存后, 其他线程才能访问到. jmm怎么体现 有序性(Ordering) ? 通过java提供的同步机制或volatile关键字, 来保证内存的访问顺序.
  • 9. 2.1. Ordering&visibility 程序顺序 程序声明的顺序 执行顺序 JMM不保证线程对变量操作发生的顺序和被其他线程看到的是同样的顺序。 JMM容许线程以写入变量时所不相同的次序把变量存入主存 线程内部本身遵循程序顺序,从线程外看到的是执行顺序 编译器和处理器可能会为了性能优化,进行重新排序 程序执行为了优化也可能重新排序
  • 10. 2.1. Ordering&visibility 多线程场景分析  class Simple {      int a = 1, b = 2; //Thread 1 executes      synchronized void to() {      a = 3; //This can appear to happen second      b = 4; // This can appear to happen first      } //Thread 2 executes      void fro()      System.out.println("a= " + a + ", b=" + b);      }     } 下面哪种结果是正确的: 1、a=1, b=2 2、a=1, b=4 3、a=3, b=2 4、a=3 ,b=4
  • 11. 2.2. Happens-Before Memory Model类似释放一致性模型 Partial ordering(happens-before) :如果B能够看到A动作产生的结果,我们说A happens-before B,JMM定义了一些这样的规则,如: Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order. Monitor lock rule. An unlock on a monitor lock happens-before every subsequent lock on that same monitor lock. Volatile variable rule. A write to a volatile field happens-before every subsequent read of that same field …
  • 12. 2.3. The rules for happens-before带锁的线程和内存交互行为 (1) 获取对象监视器的锁(lock) (2) 清空工作内存数据, 从主存复制变量到当前工作内存, 即同步数据 (read and load) (3) 执行代码,改变共享变量值 (use and assign) (4) 将工作内存数据刷回主存 (store and write) (5) 释放对象监视器的锁 (unlock)
  • 13. 2.4. Java Memory Model相关术语Thread working copy memory 在java规范中这是一个抽象的概念,对应可能会是寄存器,cpu缓存,编译及执行优化等 。 一个新产生的Thread有一个空的working memory。 线程之间无法相互直接访问,变量传递均需要通过主存完成 The main memory java堆内存 Thread's execution engine 保证线程的正确执行顺序
  • 14. 2.5. JVM原子行为定义JLS中对线程和主存互操作定义了6个行为: load,save,read,write,assign , use Intra-thread action A use action (by a thread) transfers the contents of the thread's working copy of a variable to the thread's execution engine. An assign action (by a thread) transfers a value from the thread's execution engine into the thread's working copy of a variable.
  • 15. 2.5. JVM原子行为定义 Inter-thread action A read action (by the main memory) transmits the contents of the master copy of a variable to a thread's working memory for use by a later load operation. A load action (by a thread) puts a value transmitted from main memory by a read action into the thread's working copy of a variable. A store action (by a thread) transmits the contents of the thread's working copy of a variable to main memory for use by a later write operation. A write action (by the main memory) puts a value transmitted from the thread's working memory by a store action into the master copy of a variable in main memory.
  • 16. 2.6. 案例分析例子: class Simple {      int a = 1, b = 2; //Thread 1 executes      void to() { // Out-of-Order Writes      a = 3; b = 4;      } //Thread 2 executes      void fro() {      System.out.println("a= " + a + ", b=" + b);      } }
  • 17. 2.6. 案例分析class SynchSimple { int a = 1, b = 2; synchronized void to() { a = 3; b = 4; } void fro() { System.out.println("a= " + a + ", b=" + b); } } ----下面说说关于线程安全的话题
  • 18. 二、线程安全1、编写线程安全的代码 2、设计线程安全的类 3、如何描述线程安全性
  • 19. 1、编写线程安全的代码线程安全的代码 不要跨线程共享变量 使状态变量为不可变的 在任何访问状态变量的时候使用同步。 每个共享的可变变量都需要由唯一一个确定的锁保护。而维护者应该清楚这个锁 本质上就是管理对共享的、可变的状态的访问---设计线程安全的类
  • 20. 2、设计线程安全的类确定哪些是可变共享变量 每个共享变量只有唯一一把锁来保护. 别忘了类似”get”的读操作可能也需要同步 确定哪些是不可变的变量 制定管理并发访问对象状态的策略 ----如何描述线程安全性
  • 21. 3、如何描述线程安全性五类线程安全性 不可变 线程安全 不需要任何额外的同步 有条件线程安全:最常见的例子是 Hashtable 对于单独的操作可以是线程安全的 方法之问的状态依赖需要外部同步 线程兼容: 不是线程安全的,但可通过正确使用同步而安全地使用 线程对立 不管是否调用了外部同步都不能在并发使用时安全地呈现的类
  • 22. 三、正确的理解synchronizedsynchronized 作用说明 Synchronized内存模型语义分析 如何判定多线程的程序何时需要Synchronized 同步需求的JMM内存交互分析练习 锁对象的引用在同步块中发生修改会出现什么? Hostspot JVM的synchronized优化技术 直接使用synchronized 不足之处
  • 23. 1、synchronized 作用说明Low level locking 每个对象都有一个相关的lock对象(监视器) java语言没有提供分离的lock和unlock操作 ,但是在JVM提供了两个单独的指令monitorenter和monitorext来实现 特性 Atomicity :Locking to obtain mutual exclusion Visibility :Ensuring that changes to object fields made in one thread are seen in other threads(memory) Ordering: Ensuring that you aren’t surprised by the order in which statements are executed Blocking:Can’t interrupt
  • 24. 2、Synchronized内存模型语义分析Synchronized:通过对象引用找到同步对象,然后获取对象上的监视器锁 当线程进入synchronized 块之后: 清洗 thread‘s working memory 变量赋值: 对块内使用到的变量执行assign动作 使用变量: 对use的变量执行 read->load原子操作 当线程退出synchronized 块之前,对它在working memory中所有的 assigned values执行 store –> write原子操作,写回main memory
  • 25. 2、Synchronized内存模型语义分析Example: // block until obtain lock synchronized(obj) { // get main memory value of field1 and field2 int x = obj.field1; int y = obj.field2; obj.field3 = x+y; // commit value of field3 to main memory } // release lock
  • 26. 3、 锁对象的引用在同步块中发生修改会出现什么? public void foo(int isze){ synchronized(intArr){ if(intArr.length < size){ int []newIntArr = new int[size]; System.arraycopy(intArr,0,newIntArr,0,intArr.length); intArr = newintArr; } … } … }
  • 27. 4、synchronized 不足之处和发展不能够跨越多个对象 当在等待锁对象的时候不能中途放弃,直到成功 等待没有超时限制 不能中断阻塞 JDK5中提供更加灵活的机制:Lock和Condition synchronized在JDK6中性能有很大提升
  • 28. 5、Synchronized优化技术锁省略:锁对象的引用时线程本地对象(线程的堆栈内的对象) public String getStoogeNames() { Vector v = new Vector(); v.add("Moe"); v.add("Larry"); v.add("Curly"); return v.toString(); }
  • 29. 5、Synchronized优化技术锁粗化:锁粗化就是把使用同一锁对象的相邻同步块合并的过程 public void addStooges(Vector v) { v.add("Moe"); v.add("Larry"); v.add("Curly"); }
  • 30. 5、Synchronized优化技术自适应锁优化技术 实现阻塞有两种的技术,即让操作系统暂挂线程,直到线程被唤醒,或者使用旋转(spin) 锁。旋转锁基本上相当于以下代码: while (lockStillInUse) ; Hotspot JVM可以对持有时间短的锁使用旋转,对持有时间长的锁使用暂挂。
  • 31. 6、理解和使用Volatile变量Volatile变量的内存模型分析 使用valatile JDK5的util.concurrent.atomic
  • 32. Volatile变量的内存模型分析旧的内存模型:保证读写volatile都直接发生在main memory中,线程的working memory不进行缓存 —— 仅仅保证这些volatile使用的价值和意义不大 在新的内存模型下对volatile的语义进行了修补和增强 如果当线程 A 写入 volatile 变量 V 而线程 B 读取 V 时,那么在写入 V 时,A 可见的所有变量值现在都可以保证对 B 是可见的。结果就是作用更大的 volatile 语义,代价是访问 volatile 字段时会对性能产生了一点点的影响。(A volatile var write happens-before read of the var)
  • 33. Volatile变量的内存模型分析volatile 的旧语义只承诺正在读和写的变量的可见性,仍然参与排序。这样导致排序问题。新的内存模型修补了这一点 实际上,对 volatile 字段的每一次读或者写都像是“半个”同步。 对 volatile 的读有与monitor enter的内存语义 对 volatile 的写有与monitor exit的同样的语义
  • 34. Volatile变量的内存模型分析stop用volatile修饰来保证写可见性 class Task implements Runnable { private volatile boolean stop = false; public void stop() { stop = true; } public void run() { while (!stop) runTask(); try { Thread.sleep(100); } …; } private void runTask() { /*...*/ } }
  • 35. Volatile变量的内存模型分析ready用volatile修饰来保证写可见性
  • 36. 使用valatile volatile 变量++操作不是原子行为 volatile int x= 1; … x++;//不是一个原子操作,需要多条指令 Volatile 变量比 synchronization要便宜很多 在jdk5中推荐采用 util.concurrent.atomic 工作机制类似Volatile 同时提供了简单运算的原子行为
  • 37. 并发程序设计的几个策略如何安全可靠取消正在执行的任务 如何安全可靠结束正在执行的线程 如何处理InterruptedException
  • 38. 并发程序设计的几个策略为什么需要取消正在执行的任务 User-requested cancellation Time-limited activities Application events Errors Shutdown Java没有提供安全的强迫立即执行的方法 Java提供了一种协商的机制:Interruption
  • 39. Interruption的设计策略How: 其他线程如果发起中断请求 When 何时检测正在执行的任务被请求中断 What 如何响应中断请求----下面看一个简单的例子
  • 40. 例子:这样做有何缺点?class Task implements Runnable { private volatile boolean stop = false; public void stop() { stop = true; }//how // when public void run() { while (!stop){ runTask(); } try { Thread.sleep(100); } …; } private void runTask() { … } }
  • 41. 例子分析问题: polling loops,只有一个检查点 线程不能立刻结束,要等到检测的时候 遇到Blocking method有可能无法中止 检查的变量必须是volatile修饰的
  • 42. 一个更好的方法:Interruption 礼貌地劝告另一个线程在它愿意并且方便的时候停止它正在做的事情。 Thread中断状态(interrupted status) 线程的一个内部属性 类型:boolean 初始值:false. Thread.interrupt():设置interrupted status 为true---- How?When?What?
  • 43. interruption Policy 分析How Thread.interrupt()方法. interrupt() 只是设置线程的中断状态。表示当前线程应该停止运行。 When 在 Thread.sleep()、 Thread.join() 或 Object.wait()等方法中取消阻塞并抛出 InterruptedException,也可以程序检测: 轮询中断状态:Thread.isInterrupted() 读取并清除:Thread.interrupted() InterruptibleChannel (java.nio):Most standard Channels implement InterruptibleChannel 等待获取文件锁定时可以被另一个线程中断
  • 44. Interruption Policy 分析其他问题 不能打断一些IO操作,比如文件操作 无法终止在synchronized块上锁住的Thread Synchronous socket I/O in java.io
  • 45. InterruptedException 几个概念 检查型异常:java.lang.InterruptedException 当线程在一段时间内一直处于正在等待、休眠或暂停状态(Object.wait(), Object.wait(long), Object.wait(long, int), Thread.sleep(long)),而另一个线程用 Thread 类中的 interrupt 方法中断它时,抛出该异常。 阻塞(blocking)方法 方法签名包括抛出 InterruptedException,调用一个阻塞方法则意味着这个方法也是一个阻塞方法 当中断阻塞方法时,抛出InterruptedException Thread 在 Thread.sleep() 和 Object.wait() 等方法中支持的取消机制,表明可以提前返回。 当一个阻塞方法检测到中断并抛出 InterruptedException 时,它清除中断状态
  • 46. 如何处理InterruptedException错误的做法:swallow interruption requests try { Task task = queue.take(); task.execute(); } catch (InterruptedException e) { log.error(…); }
  • 47. 如何处理InterruptedExceptionRestore the interrupt 如果真的要湮灭InterruptedException,应该保留被别人中断的证据,交给高层去决定。 //恰当的做法: try { Task task = queue.take(); task.execute(); } catch (InterruptedException e) { log.error(…); Thread.currentThread().interrupt(); } Propagate the InterruptedException 重新抛出它 ,如果抛到顶层,可以结束当前线程
  • 48. util.concurrent概览
  • 49. util.concurrent概览Executor Framework Executor :Tasks to be executed in the submitting thread A single background thread A newly created thread A thread pool A customized implementations of Executor Framework invocation, scheduling, execution, and control
  • 50. util.concurrent概览Atomic Variables get() 具有读取 volatile 变量的内存效果。 set() 具有写入(分配) volatile 变量的内存效果 支持操作: getAndSet getAndAdd addAndGet getAndDecrement getAndIncrement decrementAndGet incrementAndGet … 只要你有足够的能力,用Atomic变量能够实现所有的同步
  • 51. util.concurrent概览Synchronizers 如: semaphores, barriers, latches, and exchangers Locks a high-performance lock implementation supports specifying a timeout when attempting to acquire a lock multiple condition variables per lock non-nested support for interrupting threads
  • 52. util.concurrent概览Concurrent Collections 比如:BlockingQueue ConcurrentMap CopyOnWriteArrayList Nanosecond-granularity timing 在BlockingQueue.offer, BlockingQueue.poll, Lock.tryLock, Condition.await, and Thread.sleep等超时方法中使用
  • 53. 关于SEDA将逻辑处理划分为多个Stage 组成:Task, Queue, Thread Pool 不同的Stage有自己独特的资源使用偏好 两个常见的结构:串行和并行
  • 54. 几点建议开发多线程系统一定要理解java的内存模型,那样你才能够正确地分析并发需求 尽量使用现成的解决方案,util.concurrent 优化的前提是保证程序的正确性,其次才是提高程序的性能 缩小锁的作用范围 缩短锁的存在时间