多线程基础
Contents
同步和异步
同步和异步通常用来形容一次方法调用,同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为,异步方法更像是一个消息传递,,一旦开始,方法调用就会立即返回,调用者就可以继续后面的工作,而异步方法通常在另外一个线程中真实的执行,整个过程,不会阻碍调用者的工作.
阻塞与非阻塞
阻塞和非阻塞通常用来形容多线程间的相互关系,比如一个线程占用了临界区资源,那么其他所有需要这个资源的线程都必须啊在这个临界区中进行等待,等待会导致线程挂起,这种情况就是阻塞.
并发模型
并行工作者
传入的作业被分配到不同的工作者上。
并行工作者模型中,委派者将传入的作业分配给不同的工作者,每个工作者完成任务,工作者们并行运行在不同的线程上,甚至可能不同的CPU上。
流水线模式
每个工作者只负责作业中的部分工作,当完成了自己的这部分工作时工作者会将作业转发给下一个工作者,每个工作者在自己的线程中运行,并且不会和其他工作者共享状态。
采用流水线并发模型的系统有时候也被称为反应器系统或者事件驱动系统。
竞态条件与临界区
在同一个程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。
当两个线程竞争同一资源时,如果在资源的访问顺序敏感,就称存在竞态关系,导致竞态条件发生的代码区就成为临界区。
线程安全与共享资源
允许被多个线程同时执行的代码也被叫做线程安全的代码,线程安全的代码不包含竞态条件。当多个线程同时更新共享资源时会引发竞态条件。
局部变量
局部变量存储在线程自己的栈中,所以局部变量永远不会被多个线程共享。所以基础类型的局部变量是线程安全的。
局部的对象引用
对象的局部引用和基础类型的局部变量不太一样,尽管引用本身没有被共享,但引用所指的对象并没有存储在线程的栈中。对象都存储在共享堆中。如果在某个方法中创建的对象不会逃逸出(注:即该对象不会被其它方法获得,也不会被非局部变量引用到)该方法,那么它就是线程安全的。实际上,哪怕将这个对象作为参数传给其它方法,只要别的线程获取不到这个对象,那它仍是线程安全的。
对象成员
对象成员存储在堆上,如果两个线程同时更新同一个对象的同一个成员,那么代码就不是线程安全的。
线程控制逃逸规则
1 | //线程控制逃逸规则可以帮助你判断代码中对某些资源的访问是否是线程安全的。 |
线程安全及不可变性
多个线程同时访问一个资源,并且一个或多个进行了写操作,才会产生竞态条件,多个资源同时读时不会产生竞态条件。
1 | 通过创建不可变的共享对象来保证对象在线程间共享时不会被修改 |
引用不是线程安全的
即使一个对象是线程安全的不可变对象,指向这个对象的引用也可能不是线程安全的。
as-if-serial
意思是不管怎么重排序,程序的执行结果不能被改变,编译器,runtime,和处理器都必须遵守as if serial 语义。
volatile 写 读的内存语义
写的内存语义是:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中。
读的内存语义是:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。
锁的内存语义
锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送信息。
当线程释放锁后时,JMM会把该线程对应的本地内存中共享变量刷新到主内存中。
当线程获取锁后,JMM会把该线程对应的本地内存设置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。
何时释放锁
在以下情况下,持有锁的线程会释放锁:
\1. 执行完同步代码块。
\2. 在执行同步代码块的过程中,遇到异常而导致线程终止。
\3. 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进行对象的等待池。