第12章内存模型和线程的知识点记录 13章线程安全与锁优化知识点记录

1.课本362页:Java虚拟机规范试图定义一种Java内存模型(JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台都能达到一致的内存访问效果。

2:课本363页:Java内存模型规定了所有的变量都存储在主内存中,每条线程都有自己的工作内存。线程的工作内存保存了被该线程使用到的变量的主内存副本拷贝。

3.课本366页:volatile关键字的两种特征:

第一:可见性:保证此变量对所有线程的可见性,可见性指的是当一条线程改变了这个变量的值,新值对于其他线程来说时可以立即得知的。

第二:禁止指令重排,课本369页

指令重排序会考虑指令之间的数据依赖性,如果一个指令2必须用到指令1的结果,那么处理器保证指令1在指令2之前执行。



指令重排不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

在Java中,对基本数据类型的变量的读取和赋值操作时原子性操作,即这些操作不可被中断,要么执行,要么不执行。

只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。

注意:在32位的平台下,对64位数据的读取和赋值是需要通过两个操作来完成的,不能保证其原子性。但是在最新的JDK中,JVM已经保证对64位数据的读取和赋值也是原子性操作了。


课本386页:线程安全限定于多个线程之间存在共享数据访问这个前提,因为如果一段代码根本不会与其他线程共享数据,那么从线程安全的角度来看,程序时串行执行还是并行执行对它来说完全没有区别。所以不可变的对象一定是线程安全的。

如果共享的数据时基本数据类型,那么就在定义的时候使用final关键字修饰,线程外定义一个变量,线程里面操作一般都只能设置为final。

如果共享的数据时对象,那么就把对象中带有状态的变量都声明为final。

互斥同步又称为阻塞同步:

课本391页:互斥同步最基本的手段就是synchronized关键字,在执行monitorenter指令的时候,首先尝试获取对象的锁,如果这个对象没有锁定,或者当前线程已经拥有了哪个对象的锁,把锁的计数器加 1,相应的执行monitorexit指令时会将锁减去1,当计数器为0时,锁就会被释放。如果获取对象锁失败,那么当前线程就进入同步队列,直到对象锁被另一个线程释放为止。

课本391页:synchronized为什么称为重量级锁:因为Java的线程都是映射到操作系统的原生线程上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多处理器时间(其实线程池也是为了解决这个问题,每次创建、销毁线程也需要耗费处理器时间)。所以一种优化时在通知操作系统阻塞线程前加入一段自旋等待过程,避免频繁地切入到核心态中。

课本392页:ReetrantLock和synchronized区别:增加了等待可中断、可实现公平锁、可以绑定多个条件

等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事务

公平锁:公平锁指的时按照申请锁的时间顺序一次获得锁。

锁绑定多个条件:一个ReentrantLock对象可以绑定多个Condition对象。应用生产消费者问题:生产者可以只唤醒消费者,消费者可以只唤醒生产者。

非阻塞同步:基于冲突的乐观锁侧率:就是先执行操作,如果没有其他线程争用共享数据,那操作就成功了,如果共享数据有争用,产生了冲突,那就不断地重试,直到成功为止,这种乐观的并发策略不需要将线程挂起。

课本393页:乐观锁需要操作和冲突检测这两步具备原子性。

CAS指令需要3个操作数,分别对应内存位置(理解为变量的内存地址,用v表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,当且仅当v符合预期值A时,处理器用新值B更新v的值,否则它就不执行更新,但是无论是否更新了v的值,都会返回v的旧值。

课本396页:incrementAndGet()函数,首先获取current值,在进行current+1的过程中,如果current的值被其他的线程改变了,就一直循环执行这步,直到完成操作。如果current的值没有被其他线程改变,此时就将current+1的值赋值给current。

课本396页:CAS操作可能会导致ABA问题,这个问题可以通过控制变量值的版本来保证CAS的正确性。

课本397页:锁优化技术:如适应性自旋、锁消除、锁粗化、轻量级锁、偏向锁等。

课本398页:挂起线程和恢复线程的操作需要转入内核态完成,这些操作很消耗系统的资源。在一些应用中,共享数据的锁定状态只会持续很短的一段时间,为了这段时间去挂起和恢复线程并不值的。我们可以让等待锁的线程不放弃处理器的执行时间,等待持有锁的线程释放锁。默认的自旋值为10次。(疑问:如果时一个处理器,开启两个线程,此时就不能使用自旋锁吗

课本400页:如果一系列的连续操作都对同一对象反复的加锁和解锁,甚至加锁操作时出现的循环体中,此时频繁的互斥同步操作也会导致不必要的损耗。




猜你喜欢

转载自blog.csdn.net/chenkaibsw/article/details/80884332