java内存模型与线程

java内存模型与线程

  • java内存模型

主要目标:是定义程序中的各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出到变量这样的底层细节。(变量是指实例字段 静态字段 构成数组对象的元素,但是不包括局部变量跟方法参数,这两个是线程私有的,不会被共享不存在竞争问题)

规则:
  1. 所有的变量都存储在主内存中。
  2. 每条线程都存在自己的工作内存,线程中的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取,复制都必须在工作内存中进行,而不能直接读取主内存中的变量。)
  3. 不同间的线程之间也无法直接相互访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
    java内存模型与线程
  • 内存间交互操作

主内存跟工作内存间具体的交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回到主内存之类的实现细节。

  • java内存模型定义了八种操作来完成

虚拟机现实时必须保证下面提及的每一个操作都是原子的,不可再分的(对于double和long类型的变量来说,load、store、read、write操作再某些平台上允许有例外)

1. lock(锁住)
作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
2. unlock(解锁)
作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
3. read(读取)
作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作作用。
4. load(载入)
作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
5. use(使用)
作用于工作内存的变量,它把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的字节码指令时将会执行这个操作。
6. assign(赋值)
作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
7. store(存储)
作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
8. write(写入)
作用于主内存的变量,它把store操作从工作内存中得到的变量的值放到主内存的变量中。**
java内存模型与线程
java内存模型与线程

  • 八种操作的基本规则:
  1. 不允许read和load,store和write操作之一单独出现。即不允许一个变量从主内存读取了但是工作内存不接受,或者从工作内存发起回写了但是主内存不接受的情况出现。
  2. 不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
  3. 不允许线程无原因地把数据从线程的工作内存同步回主线程中。
  4. 一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化的变量,换句话说 就是对一个变量实施use store操作之前,必须先执行过了assign和load操作。
  5. 同一个变量在同一个时刻只允许一条线程对其进行lock操作,但是lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁
  6. 如果对一个变量执行load操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
  7. 如果一个变量没有进行load操作就不可以进行unload操作,也不可以去unlock一个被其他线程lock的变量
  8. 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store write 操作)
  • 先行发生原则

  1. 程序次序规则
    一个线程内代码的先后循序执行
  2. 管程锁定原则
    同一把锁,一个unlock操作先行发生于后面对同一个锁的lock操作。
  3. volatile变量规则
    对于一个volatile变量的写操作先行发生于后面对这个变量的读操作。
  4. 线程启动规则
    THread对象的start()方法先行于此线程的每个方法
  5. 线程终止规则
    线程的所有操作都先行发生于此线程的终止检测,我们可以通过Thread.join()方法结束,Thread.isAlive()的返回值等手段检测到线程已经终止执行
  6. 线程中断规则
    对于线程的interrupt()方法的调用先行发生于被中断线程的代码检测到中断时间的发生,可以使用Thread.interrupted()方法检测到是否有中断发生。
  7. 对象终结规则
    一个对象的初始化完成(构造函数结束)先行于它的finalize()方法的开始
  8. 传递性
    如果操作A先行发生于操作B,操作B线程先行发生于操作C,那就可以得出操作A先行于操作C
  • 线程调度

系统为线程分配处理器使用权的过程

1. 同式线程调度

线程把自己的工作执行完了要主动通知系统切换到另一个线程上。
好处:实现简单
坏处:线程执行时间不可控,一旦出现问题,并不会通知系统切换线程,程序一直阻塞在那里

2. 抢占式线程调度

每个线程系统来分配时间,线程的切换不由线程本身决定。(可以通过优先级(但是只是建议并不是一定靠谱))

  • 线程的状态

1. 新建(New)

创建后尚未启动的线程处于这种状态

2. 运行(Runable)

包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能在等待cpu为它分配执行时间

3. 无限等待(Waiting)

处于这种状态下的线程不会被分配CPU执行时间,他们等待被其他线程显示地唤醒。

调用方法:
1没有设置Timeout参数的Object.wait()方法。
2 没有设置Timeout参数的Thread.join()方法。
3 LockSupport.park()方法。

4. 限期等待 (Timed Waiting)

处于这种状态的线程也不会被分配cpu执行时间,不过无须等待被其他线程显示地唤醒,在一定时间之后它们会有系统自动唤醒。

调用方法:

  1. Thread.sleep()方法
  2. 没有设置Timeout参数的Object.wait()方法。
  3. 没有设置Timeout参数的Thread.join()方法。
  4. LockSupport.packNanos()方法。
  5. LockSupport.packUntil()方法。
5. 阻塞(Blocked)

线程被阻塞了。阻塞跟等待两个状态的区别是:阻塞:在等待获取到一个排他锁,这个事件将在另一个线程放弃这个锁的时候发生;等待:则是等待一段时间或者唤醒动作的发生。 在程序等待进入同步区域的时候,线程将进入阻塞这种状态。

6. 结束(Terminated)

已终止程序的线程状态,线程已经结束执行
java内存模型与线程
java内存模型与线程

参考:
《深入理解Java虚拟机》
部分网上文章
如有侵权请联系本人