java并发编程实战-第二章
序文:(由于格式问题重新发一下)
如果你没有时间阅读这本,那你就来对地方了。小编会将每一章刨去废话,取其精华。分享给大家。如果觉得可以请不要忘了关注小编。我会定期跟新java 中的经典书籍。
正文:
第二章:线程安全性 (这一章多为概念记住就好,没有详细介绍)
1.一个对象是否需要考虑线程安全,取决于他是否被多个线程访问。
2.要使得对象线程安全,需要采用同步机制来控制对对象可变状态的访问。
-
对象的状态:除去对象的方法。其余的变量,对其他的对象的引用等。
3.可变状态:非final修饰的属性
4.java 中的同步的实现方式(先罗列):
-
synchronized,它提供了一种独占的加锁方式,
-
volatile
-
显示锁
-
原子变量:AtomicInteger AtomicLong ..(java 中多以Atomic开头的类)
5.如果当多个线程访问同一个可变状态(可以简单理解为全局变量)时,有三种方法来实现线程安全:
-
不在线程之间共享该状态变量
-
将状态变量修改为不可变的变量(final)
-
在访问状态变量时候使用同步(最符合实际)
6.程序状态的封装性越好,就越容易实现程序的线程安全。(就是不该共享的不共享)
7.何为线程安全性:当多个线程访问某个类时,这个类始终能表现出正确的行为,那么就称这个类是线程安全的。
-
在使用这个类的的时候不需要使用额外的同步代码(类的内部使用同步已经确保类的线程安全,比如 原子变量类)
8.无状态的对象一定是线程安全的。
-
无状态对象:不包含任何域,也不包括对其他类中域的引用。(即只有方法,没有全局的属性变量)
-
无状态多线程之间没有共享的变量,所以各自做各自的事情互不干扰
9.复合操作
-
++ 操作为例:可以分为三步 读取-修改-写入 一旦是多个操作组成,线程在执行一个操作完成后就可能,被切换到执行其他的事情。
-
这样就可能产生静--竞态条件:由于不恰当的执行时序而出现不正确的结果
-
产生场景:有先后顺序的执行代码中,如果满足某种条件就执行(先检测后执行)
10.如何避免竞太条件问题:
-
必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量(原子方式执行)
11.在无状态类中添加一个线程安全对象的属性,那么这个类任然是线程安全的。
-
比如添加一个原子变量(AtomicInteger )这个类任然是线程安全的。(只能是一个 ,如果添加多个就不一定了)
12.原子变量只适合少量(一个是最佳),如果多个就要考虑使用加锁机制了(且最好不要混用)。
13.内置锁:同步代码块(使用内置锁机制来支持原子性)
-
同步代码块包括两个部分:
-
锁的对象引用
-
这个锁保护的代码块
-
用关键此 synchronized 来修饰:
-
直接用在方法定义上 ,那么这这个方法体整个都是原子操作,锁对象是调用这个方法的对象(如果用在静态方法上,锁对象是这个类的Class对象)。
-
每个java 对象都可以用做一个实现同步的锁,这些锁被称为 内置锁或监视器锁。
-
线程进入同步代码块时获得锁对象,保存在jvm中
-
退出时(正常和异常退出都是)释放锁
-
获得锁的唯一途径就是进入同步代码块,或者同步方法。
14.锁的重入
-
何为重入:获取锁的线程,再一次获取同一个锁,
15.如何实现的:
-
当一个线程获取一个锁的时候,jvm会记录下锁的持有线程,并且将创建一个计数器,计数器初始是0,表示没有任何线程持有该锁。如果是1有线程进入到同步代码块中;如果是2说明这个线程重入了。
-
即进入+1,退出-1
-
只有计数器为0的时候才说明这个锁完全释放了。
-
例子:
-
父类中有一个方法是同步方法
-
子类是一个同步方法调用了父类的同步方法。
注意:
-
扩展一下,super 和this 都是同一个子类对象。(所以获取的锁都是子类对象)
16.同步可以保证线程安全但是不能滥用:
-
一个方法使用同步那么这个方法就不可以被并发执行,导致多个线程在等待cpu处于空闲
17.何时使用同步:
-
共享变量的访问和写入(不要错误的理解只有写入)
-
一个共享的变量所在的所有的地方都需要同步,并且用的是同一个锁。
18.如何合理的使用同步(总结)
-
同步代码块尽可能小
-
不需要的同步的代码尽量分离出来不要放在代码块中
-
只有一个共享变量的时候使用原子变量
-
不要混用同步代码块和原子变量
-
执行时间长的操作(网络I/O ,控制台I/O)不要持有锁