《Java并发编程实战》学习笔记
Posted __Meng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java并发编程实战》学习笔记相关的知识,希望对你有一定的参考价值。
二、线程安全性
正确性:
某个类的行为与其规范完全一致。
线程安全:
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类就能表现出正确的行为,那么就称这个类是线程安全的。
无状态对象:
既不包含任何域,也不包含任何其他类中域的引用的对象。
无状态对象一定是线程安全的。
竞态条件:
当某个计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。
本质是基于一种可能失效的观察结果来做出判断或者执行某个计算。
最常见的竞态条件类型就是“先检查后执行(Check-then-Act)”。
“读取-修改-写入”操作也是一种竞态条件。
原子性:
假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说是原子的。
原子操作:
对于访问同一个状态的所有操作(包括操作本身)来说,这个操作是一个以原子方式执行的操作。
要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。
内置锁:
每个Java对象都可以用作一个实现同步的锁,这些锁被称为内置锁(Intrinsic Lock)或者监视器锁(Monitor Lock)。
内置锁是一种互斥锁,最多只有一个线程持有这个锁。
同步代码块(Synchronized Block)包括两部分:
一个作为锁的对象引用,一个作为由这个锁保护的代码块。
关键字Synchronized修饰方法就是一种同步代码块,锁就是方法调用所在的对象,静态的Synchronized方法以Class对象作为锁。
重入:
因为内置锁是可重入的,所以如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功。
重入意味着获取锁的操作粒度是“线程”,而不是“调用”。
实现方式:为每个锁关联一个获取计数值和一个所有者线程。当计数值为0时,这个锁被认为没有被任何线程持有。当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1。如果同一个线程再次获取这个锁,计数值将递增,而当线程退出同步代码块时,计数值减1。
重入提升了加锁行为的封装性,因此简化了面向对象并发代码的执行。
用锁来保护状态:
锁能够使其被保护的对象以串行方式来执行。
对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,在这种情况下,我们称状态变量是由这个锁保护的。
对象的内置锁与其状态之间没有内在的联系,虽然大多数类都将内置锁用做一种有效的加锁机制,但对象的域并不一定要通过内置锁来保护。
一种常见的加锁约定:将所有可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路径进行同步,使得该对象上不会发生并发访问。
对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护。
活跃性与性能:
将同步代码块分解得过细并不好,因为获取与释放锁操作需要开销。
当执行时间较长的计算或者可能无法快速完成的操作时(如I/O),一定不要持有锁。
同时使用两种不同的同步机制会带来混乱,在性能或安全性上也没有任何好处。(如内置锁synchronized和Atomic原子变量) 。
以上是关于《Java并发编程实战》学习笔记的主要内容,如果未能解决你的问题,请参考以下文章