深入理解jvm(五):java内存模型和线程
内存模型
为了实现高效的并发,java的内存分为主内存和工作内存(类比于计算机的内存和缓存模型)。每一条内存都有一块自己的工作内存,并管理自己的工作内存。如果线程间需要共享数据则使用 store 和 load操作去主内存区进行数据的读写。在每一次线程超过工作内存往主存读写数据时,都会换得系统的管程,以保证读写操作的互斥性。
除了上文提及的store 和 load 操作外,还有lock & unlock,read & load ,use & assign ,store & write等操作,分别来实现数据在工作内存和主内存区的数据交互。在使用这些操作时,使用先行发生原则,是确定一个访问环境是否安否的前提。
先行发生原则简述:
1.程序次序原则:按照控制流,书写在前的先行与书写在后的
2.管程锁定规则:对通过一个锁,unlock先行发生于下一个lock
3.volatile原则:对于volatile数据写操作先行与之后的读操作
4.线程启动原则:star()方法先行与线程执行的任何方法
5.线程终止原则:线程的任何方法先行于此线程的终止检测isAlive()
6.线程中断原则:对线程方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted方法检测是否有中断线程发生
7.对象终结原则:对象的所有操作先行发生于对象的终结操作finalize()
8.传递性原则:A先行发生于B,B先行发生于C,则A先行发生于C
此外,先行发生与时间的先后发生不同,先行发生不一定就是时间上先发生,时间上先发生也无法保证先行性
special1:volatile的语义包括两个,一是被volatile修饰的变量发生变化时,其他线程能够立即感知。二是被volatile修饰的变量不会发生重排序优化
special2:对于未被volatile修饰的long和double型变量,被当做两个32位操作数来操作。这个过程有可能会破坏交互行为的原子性(在商业虚拟机中出现的概率极小)
java与线程
使用内核线程实现
每一个操作系统内核KLT对应一个轻量级进程LWP,轻量级进程就是我们通常意义上的线程。优势:即使一个lwp被阻塞也不会影响整个整个进程的工作。劣势:由于线程直接作用在内核上,势必要频繁的进行管态和目态的切换,带来较大的额外开销。
使用用户进程实现
所有线程的创建,销毁都在目态上进行,减少的开销。但是所有进程的创建,销毁也都要自己实现,较为复杂,甚至无法实现。
混合实现
既存在用户线程又存在内核线程,保持了两者的优势,同时客服了两者的劣势
对java来说,jdk1.2以前是1:N模型,jdk1.2以后Windows平台和linux平台是1:1模型,solaris平台既可能是1:1,又可能是N:M
线程调度方式
协同式:一个线程执行完之后通知另一个线程继续执行。可能因为阻塞而不通知切换程序导师系统崩溃
抢占式:每个线程由系统分配运行时间,时间到了没有执行完直接kill