12.Java内存模型与线程

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量与Java编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数组对象的元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不存在竞争问题。

Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以互相类比,但此处仅是虚拟机内存的一部分)。每条线程还有自己的工作内存(Working Memory,可与处理器高速缓存类比),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。从更低层次上说,主内存就直接对应于物理硬件的内存,而为了获取更好的运行速度,虚拟机可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存。

当一个变量定义为volatile之后,它将具备两种特性,第一是保证此变量对所有线程的可见性,这里的可见性是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。第二个语义是禁止指令重排序优化,普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。

volatile屏蔽指令重排序的语义在JDK1.5中才被完全修复,此前的JDK中即使将变量声明为volatile也仍然不能完全避免重排序所导致的问题(主要是volatile变量前后的代码仍然存在重排序问题),这点也是在JDK1.5之前的Java中无法安全地使用DCL来实现单例模式的原因。

Java语言中有一个happens-before先行发生原则

  • 程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确的说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构
  • 管程锁定规则,一个unlock操作先行发生于后面对同一个锁的lock操作
  • volatile变量规则,对一个volatile变量的写操作先行发生于后面对这个变量的读操作
  • 线程启动规则,Thread对象的start()方法先行发生于此线程的每一个动作
  • 线程终止规则,线程中的所有操作都先行发生于对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行
  • 线程中断规则,对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生
  • 对象终结规则,一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
  • 传递性,如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

Java语言定义了5中线程状态:

  • 新建New,创建后尚未启动的线程处于这种状态
  • 运行Runnable,Runnable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。
  • 无限期等待Waiting,处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。一下方法会让线程陷入无限期的等待状态:
    • 没有设置timeout参数的Object.wait()方法
    • 没有设置timeout参数的Thread.join()方法
    • LockSupport.park()方法
  • 限期等待Timed Waiting:处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程进入限期等待状态:

    • Thread.sleep()方法
    • 设置了timeout参数的Object.wait()方法
    • 设置了timeout参数的Thread.join()方法
    • LockSupport.parkNanos()方法
    • LockSupport.parkUntil()方法
  • 阻塞Blocked,线程被阻塞了,阻塞状态与等待状态的区别是:阻塞状态在等待着获取一个排他锁,这个时间健在另外一个线程放弃这个锁的时候发生;而等待状态则是在等待一段时间,或者唤醒动作的时候发生。在程序等待进入同步区域的时候,线程将进入这种状态。
  • 结束Terminated:已终止线程的线程状态,线程已经结束执行。

results matching ""

    No results matching ""