第一节
- volatile与synchronized最大的区别就是volatile保证了可见性,但是不能保证原子性(线程安全),而synchronized即保证了原子性,又保证了可见性。
- volatile禁止重排序功能,但是synchronized不会禁止重排序
- volatile与synchronized的区别:(重要)
(1)synchronized保证了内存的可见性和操作的原子性
(2)volatile只能够保证线程的可见性
(3)volatile不需要加锁,比synchronized更加轻量级,并不会阻塞线程,但是synchronized可能会造成线程的阻塞。
(4)volatile标记的变量不会被编译器优化(即不支持重排序功能),而synchronized则支持重排序,可以被编译器优化。
(5)volatile是变量修饰符,仅能够修饰变量,而synchronized是一个方法或块的修饰符。 - 重排序概念:CPU会对代码实现进行优化,但是其不会对有依赖关系做重排序。(重排序如果有影响也是在多线程的情况下才会有影响),重排序会使代码的执行的顺序可能发生改变,但是执行的结果不会发生任何改变。
- 什么是数据的依赖关系:
(1)
int a = 1;
int b = 2;
int c = a*b
此时a和b的赋值就是互不影响的
第二节
- as-if-serial语义:无论怎么做重排序,但是目的都是提高并行度,不能影响到正常的结果
- 重排序一般在多线程的情况下遇到。
- 加入volatile可以禁止重排序
- 举例说明:
double pi = 3.14;// A
double r = 1.0; // B
double area = pi*r*r; // C
根据上面的代码我们可以对其进行分析,A与C之间存在依赖关系,所以C不能排到A的前面,同时B与C之间也存在依赖关系。所以C也不能排到B的前面。但是A与B之前是不存在依赖关系的,所以A与B是可以进行重排序的。
-
happens-before的定义:
(1)如果一个操作happens-before另一个操作,那么第一个操作的执行结果对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
(2)两个操作之间存在happens-before关系,并不意味着java平台的具体实现必须按照happens-before关系指定的顺序来执行,如果重排序之后的执行结果,与按照happens-before关系来执行的结果一直,那么这种重排序并不非法。
注意:
(1)上面1是JMM对程序员的承诺。从程序员的角度来说,可以这样理解happens-before关系:如果A happens-before B,那么Java内存模型将向程序员保证–A操作的结果将对B可见,且A的执行顺序排在B之前,注意,这只是Java内存模型向程序员做出的保证。
(2)上面2是JMM对编译器和处理器重排序的约束,JMM其实是在遵循一个基本的原则:只要不改变程序的执行结果,编译器和处理器怎么优化都行。happens-before这么做的目,都是为了在不改变程序执行结果的前提下,尽可能的提高执行的并行度。(保证了as-if-serial) -
happens-before规则
(1)程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
(2)监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁
(3)volatile变量规则:(volatile会使其禁止重排序)对一个volatile域的写,happens-before于任意后续对这个volatile域的读。即写发生在读之前
(4)传递性:如果A happens-before B, 且B happens-before C,那么A happens-before C
第三节
- 多线程之间的通信,wait和notify,对象锁池
- wait释放当前的锁,当前线程等待
- notify唤醒当前等待的线程
- wait和notify必须是在同步方法中使用,即synchronized中使用
- 生产者其实是是向共享数据中写操作,消费者是向共享内存中的读数据。
- 加锁的时候,不仅要在生产者加锁,消费者读数据的时候也要加锁,因为有可能读了一半,另一个线程已经修改了变量,这样子读取数据是错误的。
第四节
- wait和notify,常用的线程的通信机制
- wait和notify使用的时候一定要在同步方法或者同步代码块中。并且一定要持有同一把锁。
- wait是让当前线程等待,释放锁的资源
- notify唤醒当前对象锁池被等待的资源,这里就是必须是同一把锁的原因,释放的是当前锁对象池中所有等待的资源。
- 一般synchronized锁哪个对象,哪个对象调用wait和notify(重要)
- wait和notify都是object的方法,synchronized锁哪个对象,哪个对象调用wait和notify
- notifyall唤醒所有的当前锁池中的线程
- wait与join的区别
(1)join无需唤醒(join底层使用的是wait,也会释放锁对象)
(2)wait用在同步中 - wait与sleep的区别
(1)sleep不会释放锁
(2)sleep是Thread的方法