多线程专题
一、基础概念
1、什么是进程?什么是线程?
进程是os调度的最小单元,比如启动一个java程序就会创建一个进程,线程是cpu调度的最小单元,一个进程可以创建很多个线程,这些线程有各自的计数器、堆栈、局部变量等属性。
2、什么是JMM模型?
java内存模型(Java Memory Model),并不真实存在,描述的是一组规则或规范,这组规范定义了各个变量的访问方式。JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存,用于存储线程私有的数据,JMM规定所有变量都存储在主内存,主内存是共享区域,所有的线程都可以访问,但是线程变量的操作必须在工作内存中进行,首先将变量拷贝到自己的工作内存空间,然后进行操作,操作完成后回写主内存。不同线程之间无法访问对方的工作内存,线程间的通信必须通过主内存完成。
主内存:实例共享区域,包括类信息、常量、静态变量。由于是共享数据区域,多条线程对同一个变量进行访问时可能会发生线程安全问题。
工作内存:存储当前方法的所有本地变量信息副本,每个线程只能访问自己的工作内存,即线程中的本地变量对其他线程是不可见的,就算两个线程执行的是同一段代码。
数据同步的八大原子操作
- lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
- unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存中的变量,把read操作从主内存中获取到的变量写入到工作内存的副本中
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
- assign(赋值):作用于工作内存中的变量,把执行引擎接收到的值传送到主内存的变量
- store(存储):把工作内存中的值传送到主内存,以便后续的write操作
- write(写入):把store操作从工作内存中一个变量的值写入到主内存
同步规则分析
- 不允许一个线程无原因的(没有发生过任何assign操作)把数据从工作内存同步回主内存中
- 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化的变量。即对一个变量实施use和store操作之前,必须先自行load和assign操作
- 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一线程重复执行多次,lock与unlock必须成对出现
- 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量之前需要重新执行load和assign操作
- 如果一个变量事先没有被lock操作锁定,则不允许对他执行unlock操作
- 执行unlock之前,必须先把此变量同步到主内存中(执行store和write操作)
并发编程的可见性,原子性与有序性问题
- 原子性:是指一个操作是不可中断的,即使多线程环境下,一旦开始就不会被其它线程影响。基本数据类型都是原子操作(32为操作系统的long和double不一定,因为long和double是64位存储单元,虚拟机读取到的可能是半个变量的值)
解决:通过syncchronized和lock能保证任意时刻只有一个线程访问该代码块 - 可见性:理解指令重排之后,可见性就容易理解了,指的是,当一个线程修改了某个共享变量的值,其它线程是否能够马上得知这个修改的值
解决:volatile关键字可保证可见性,syncchronized和lock也能保证可见性,因为他们可以保证同一时刻只有一个线程能访问共享资源,并在释放锁之前将修改值刷新到主内存中 - 有序性:如果是多线程环境下,指令重排后的顺序与原指令未必一致
解决:valatile可以保证一定的有序性,syncchronized和lock可以保证有序性,相当于同一时刻只有一段程序执行同步代码,自然保证了有序性
happens-before原则
- 程序顺序原则:即在一个线程内,必须保证语义的串行性,也就是说按照代码顺序执行
- 锁规则:解锁动作必须在加锁之前,那么加锁动作也必须在解锁之后
- volatile原则:简单理解就是volatile变量在每次线程访问时,都强迫从主变量中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存中
- 线程启动规则:start方法先于他的每个动作,A在B执行start方法之前修改了共享变量的值,那么这个值对线程B可见
- 传递性:A先于B,B先于C,那么A必然先于C
- 线程终止规则:线程所有的操作先于线程的终结,Thread.join()的作用是等待当前执行的线程终止
- 线程中断规则:可以通过Thread.interrupted()方法检测线程是否中断
- 对象的终结规则:结束先于finalize()方法。
volatile内存语义
- 保证被volatile修饰的共享变量对所有的线程总数是可见的,也就是当一个线程修改了被volatile修改的变量值,新值总是可以被其他线程立即得知,但是volatile无法保证原子性
- 禁止指令重排优化
memory = allocate();//1、分配对象内存空间 instance(memory);//2、初始化对象 instance = memory; // 3、设置instance指向刚分配的内存地址
- volatile重排序规则:
- 第二个操作是volatile写时,不管第一个操作是什么,都不能重新排序。这个规则确保volatile写之前的操作不会被编译器重排到volatile读之前
- 当第一个操作时volatile读时,不管第二个操作是什么,都不能重新排序。这个规则缺包volatile读之后的操作不会被编译器重排到volatile读之前
- 第一个是volatile写,第二个是volatile读时不能重排序
岁月本长而忙者自促;天地本宽而卑者自隘;风花雪月本闲,而劳忧者自冗;天行健,君子以自强不息;地势坤,君子以厚德载物;宠辱不惊,闲看庭前花开花落;去留无意,漫随天外云卷云舒.不妄取,不妄予,不妄想,不妄求,与人方便,随遇而安