multi-threading

https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html

https://docs.oracle.com/cd/E19205-01/820-0619/geojs/index.html

http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

http://blog.csdn.net/crazyjmonkey/article/details/8538602

http://hllvm.group.iteye.com/group/topic/34975

JLS8

 

 

Threads and Locks

Java语言支持多线程,多线程程序的每个线程单独的执行代码,来操作位于主存的对象和值数据(通信)。通过时间分片技术,多个处理器、单个处理器都支持Java多线程。

在Java里Thread类表示多线程,创建Thread类的一个对象是创建多线程的唯一方式;每一个线程都关联这这样一个对象。通过调用相应Thread上的start方法来启动一个线程。

线程的行为,特别是没有正确地同步的时候,可能会令人费解和违反直觉。这章描述了多线程的语义,它包含了多线程更新了共享内存,值可能会对某个读可见的规则。因为这种规范(指多线程语义规范)类似于硬件的内存体系架构,这些语义也被称为Java编程语言内存模型(Java programming language memory model)。在没有混淆的情况下,我们称这些规则为“内存模型”。

这些语义规则没有规定多线程程序应该如何来执行,而是描述了多线程程序允许出现的行为。任何只产生被允许行为的执行策略都是可接收的执行策略。

Synchronization

Java编程语言提供了多种机制来让多线程之间通信。其中最基本的方法是利用监视器(monitor)实现的同步(synchronization)。(此处直译的,笔者认为前面两句话描述有些不妥。。。)Java里的每一个对象都关联着一个monitor,一个线程可以在monitor上lock和unlock。在某一时刻,只有一个线程可以持有一个monitor上的lock。任何其它试图lock那个monitor的线程都会被block,直到能获取那个monitor上的lock为止。一个线程t可以lock一个锁多次,每次unlock撤销一个lock操作的影响。

被同步(synchronized)的语句计算对Object对象的引用,然后试图在那个Object的monitor上执行lock操作,被同步的语句不会进一步的执行直到lock操作成功执行完成。lock被执行后,同步的语句(body)会被执行。当(body)执行完,或成功或发生意外,unlock操作会在那个monitor上自动的被执行。

同步方法被调用时,lock操作会自动被执行;方法体直到lock操作成功完成后才会执行。如果方法是实例方法(instance method),会lock被调用的方法的实例的monitor(这个实例即被执行方法体所在的对象)。如果方法是静态的(static),会lock方法被定义的class所代表的Class对象上的monitor。如果方法体执行完,或成功或发生意外,unlock操作会在那个monitor上自动的被执行。

Java编程语言不阻止也不检测死锁条件(deadlock conditions),多线程lock住多个对象的情况应该用常规的技术避免死锁,如果有必要,创建高级别的锁原语避免死锁。

其它机制,比如读写volatile变量和利用java.util.concurrent包来提供其他同步方式。

Wait Sets and Notification

每个对象除了关联一个monitor以外,还关联wait set,wait set是线程集合。

当一个对象第一次被创建,它的wait set是空的。在wait set上的添加、删除基本操作是原子操作。wait set只被Object.wait、Object.nofify和Object.notifyAll来操控。

wait set的操控会被线程的中断状态和Thread 类里的方法处理中断影响。此外,Thread类的方法sleep和join拥有的性质源自那些wait和notification动作。

 

Yield, sleep, join do not bother about locks. But Object.wait will release the lock

 

Wait

调用wait方法发生wait动作。

如果线程没有抛出InterruptedException则从wait中正常返回。

让线程t在对象m上执行了wait方法,n是t在m上执行的lock次数,会发生以下其中一个动作

  如果n是0,则抛出IllegalMonitorStateException(调用wait之前必须持有锁)

  如果是time wait,纳秒参数不在0-999999之间,或毫秒参数是负数,会抛出IllegalArgumentException

  如果线程t被中断,会抛出InterruptedException,t的中断状态置为false

  否则按以下序列执行:

    1.线程t加到对象m的wait set中,在m上执行n次unlock操作

    2.线程t不执行任何指令,直到它被在m的wait set中移除。以下任何动作都会引起移除wait set,继续执行后面的操作

      执行了m上的notify方法,t被选中,移除wait set

      执行了m上的notifyAll方法

      在t上执行了interrupt方法

      如果是time wait,指定的时间到了

    3.线程t在m上执行n次lock操作

    4.如果因为interrupt线程t在m的wait set中移除,t的interruption 状态置为false,wait方法抛出InterruptedException

 

Notification

执行notify,notifyAll必须持有锁,否则抛出IllegalMonitorStateException

 

Interruptions

 

Interactions of Waits, Notification, and Interruption

如果一个线程在等待时,interrupt和notify都发生了,它可能会:

  从wait中正常返回,还有一个pending的interrupt(换句话说,Thread.interrupted()返回true)

  从wait中返回,抛出InterruptedException

 

Sleep and Yield

Thread.sleep会使当前线程睡眠指定时间,线程不会失去lock的所有权,线程恢复执行依赖于调度

sleep和yield没有同步语义,特别的,在sleep和yield之前,编译器不会把缓存到寄存器的写同步到主存,在sleep和yield之后,编译器也不会重新加载在寄存器中缓存的数据

 

For example, in the following (broken) code fragment, assume that this.done is a non-volatile boolean field:

while (!this.done)
    Thread.sleep(1000);
The compiler is free to read the field this.done just once, and reuse the cached value in each execution of the loop. This would mean that the loop would never terminate, even if another thread changed the value of this.done.

 

Memory Model

JMM描述了程序可能的行为,只要程序的执行结果是可预测的,code都可以自由实现

这带来了很大的自由执行code转变,包括重排序和移除不必要的同步等。

Example 17.4-1. Incorrectly Synchronized Programs May Exhibit Surprising Behavior

java语言的语义运行编译器和处理器执行优化,优化和不正确的同步代码相互作用会产生看起来奇怪的行为

几种机制可以产生重排序,jit编译器, 处理器,内存

Shared Variables

线程间可共享的内存称为共享内存或堆内存,实例变量类变量和数组都存储在堆内存上;本地变量,方法参数,异常处理器参数不被线程共享,不受JMM影响

多个线程访问同一个变量,至少有一个读,这种情况称为冲突

Actions

inter-thread actions:

Read  read a variable

Write write a variable

Synchronization actions 

  volatile read 

  volatile write 

  Lock

  Unlock

  The (synthetic) first and last action of a thread.

  Actions that start a thread or detect that a thread has terminated

External Actions. An external action is an action that may be observable outside of an execution, and has a result based on an environment external to the execution.

Thread divergence actions . A thread divergence action is only performed by a thread that is in an infinite loop in which no memory, synchronization, or external actions are performed. If a thread performs a thread divergence action, it will be followed by an infinite number of thread divergence actions.

本规范只关心inter-thread actions,简称inter-thread actions为Actions

一个action通过一个元组描述<t,k,v,u>

t,执行动作的线程

k,action的种类

v,action中涉及的变量或monitor

u,action的任意唯一标志符

 

所谓intra-thread语义就是单个线程执行某条代码路径的时候,不管是否存在重排序等操作,在执行结果上都会表现出与源码一致的结果。比如这个代码路径中有一个写入变量v的操作o1,只要中间没有再写该变量,后续就能读到之前o1写的值

Programs and Program Order

program order意为线程t执行的所有的inter-thread Action都表现出了intra-thread的语义。 
每次对变量v的读操作r都能看到写操作w写入v的值,只要存在下面的条件: 
·执行顺序上w在r之前; 
·且不存在这样写操作w',执行顺序上w在w'之前,w'在r之前。 

Program Order是顺序一致(Sequential consistency)的。顺序一致性是可见性的强有力保证。

 

Synchronization Order

每个线程t中synchronization action的synchronization order是与Program Order一致的。 
Synchronization action之间的synchronized-with关系有如下几种: 
1、解锁monitor m synchronized-with 后续 锁定monitor m; 
2、volatile写变量v synchronized-with 后续对该变量的 volatile读; 
3、启动一个线程 synchronizes-with 线程中的首个action; 
4、变量写入默认值 synchronizes-with 线程中的首个action; 
5、线程t1中的最后一个action  synchronizes-with 线程t2中检测t1是否终止的action(如t1.isAlive() 或 t1.join()); 
6、若t1中断t2,该中断 synchronizes-with 任意时刻其它线程(包括t2)发现t2被中断了(通过抛出InterruptedException或调用 Thread.interrupted、Thread.isInterrupted)。

synchronized-with有向边的源叫做release,终点叫做acquire

 

Happens-before Order

两个action可以被 happens-before排序,如果一个action happens-before另外一个,则第一个对第二个是可见的
用hb(x,y)表示x happens-before y。 
1、如果x,y在同一个线程里,x在program order上在y之前,则hb(x, y). 
2、构造方法的结束 happens-before finalizer的开始。也就是说构造方法中对字段的写操作的最新值在finalize中一定是可见的。 
3、如果 x synchronizes-with y, 那么hb(x, y). 
4、如果hb(x, y)并且hb(y, z), 那么 hb(x, z). 

Executions

 

Well-Formed Executions

 

Executions and Causality Requirements

 

final Field Semantics

 

 

 

 volatile

  • 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
  • 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

final


    • 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
    • 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

 

当线程有数据依赖性的的操作不会被重排序

as-if-serial语义,单线程中不管如何重排序,程序的执行结果不会被改变,编译器,处理器都必须遵守这个语义

 

http://ifeve.com/java-memory-model-5/

锁的语义通过volatile和cas()来实现的。

cas intel x86 程序根据cpu类型决定是否在cmpxchg指令前加lock前缀

lock前缀

1.通过总线锁/缓存锁确保读写原子性

2.禁止重排序

3.刷新缓冲区到内存

 

AQS

http://ifeve.com/introduce-abstractqueuedsynchronizer/

http://ifeve.com/jdk1-8-abstractqueuedsynchronizer/

http://ifeve.com/jdk1-8-abstractqueuedsynchronizer-part2/

http://www.liuinsect.com

 

 

http://www.pwendell.com/2012/08/13/java-lock-free-deepdive.html

posted on 2014-11-13 16:00  ukouryou  阅读(331)  评论(0编辑  收藏  举报

导航