打赏

Java 并发系列之一:java 并发体系

1.  java 并发机制的底层原理实现

 

1.1 volatile

 

1.2 synchronized

1.3 原子操作

2. java 内存模型(JMM)

3. java并发基础线程

 

4. java 锁

5. java 并发容器

6. java阻塞队列(7个)

7. java 并发工具(4个)

8. java 原子操作类Atomic(13个)

9. java并发框架(2个)

 

10. txt

  1 java 并发机制的底层原理实现
  2     volatile
  3         特性
  4             volatile可见性:对一个volatile的读,总可以看到任意线程对这个变量最终的写
  5             volatile原子性:volatile对单个读/写具有原子性(64位Long、Double),但是复合操作除外,例如 i++
  6             volatile有序性:编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
  7         内存语义
  8             当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新到主内存中
  9             当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主存中读取共享变量
 10             volatile的写-读与 锁的释放-获取 有相同的内存效果:volatile写和锁的释放有相同的内存语义,volatile读和锁的获取有相同的内存语义。这也是增强volatile的原因,提供了一种比锁更轻量级的线程之间的通信机制。
 11         重排序规则
 12             1. 当第二个操作是volatile写操作时,不管第一个操作是什么,都不能重排序
 13             2. 当第一个操作是volatile读操作时,不管第二个操作是什么,都不能重排序
 14             3. 当第一个操作是volatile写操作,第二个操作是volatile读操作,不能重排序
 15         内存语义的实现
 16             内存屏障
 17                 内存屏障是一组处理器指令,用于实现对内存操作的顺序限制
 18                 基于保守策略的JMM内存屏障插入策略
 19                     在每个volatile写操作的前面插入一个StoreStore屏障
 20                         保证之前的都能刷新到主存
 21                     在每个volatile写操作的后面插入一个StoreLoad屏障
 22                         保证先写后读,能提高效率
 23                     在每个volatile读操作的后面插入一个LoadLoad屏障
 24                     在每个volatile读操作的后面插入一个LoadStore屏障
 25                 实际执行时,只要不改变volatile写-读的内存语义,编译器可以根据具体情况省略不必要的屏障
 26                 x86处理器只会对写读作重排序,故只有一个屏障StoreLoad即可正确实现volatile写-读的内存语义
 27         操作系统语义
 28             主存、高速缓存(线程私有)缓存一致?
 29             解决方案
 30                 通过在总线加 Lock 锁的方式,有些是缓存锁定
 31                 通过缓存一致性协议 MESI协议(修改、独占、共享、无效)
 32             实现原则
 33                 Lock前缀指令会引起处理器缓存回写到内存
 34                 一个处理器的缓存回写到内存会导致其他处理器的缓存失效
 35         内存模型
 36             内存屏障 限制重排序
 37             happens-before中的volatile规则
 38         volatile VS synchronized
 39             volatile是轻量级的synchronized
 40             使用恰当,它比synchronized使用和执行成本更低,因为不会引起线程上下文的切换和调度
 41     synchronized
 42         同步、重量级锁
 43         只有使用Synchronized线程处于阻塞,其他Lock, AQS, LockSupport等线程都是处于等待状态
 44         原理
 45             synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证变量的内存可见性。
 46         锁对象
 47             1. 普通同步方法锁,是当前实例对象
 48             2. 静态同步方法,锁是当前类的class对象
 49             3. 同步方法块,锁是括号里面的对象
 50             java中的每一个对象都可以作为锁
 51         实现机制
 52             Java对象头
 53                 synchronized用的锁是保存在Java对象头里的
 54                 包括两部分数据
 55                     Mark Word(标记字段)
 56                         Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据,它会根据对象的状态复用自己的存储空间
 57                         包括:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
 58                     Klass Pointer(类型指针)  
 59             monitor
 60                 java 中 Object和Class中都关联了一个Monitor,一个monitor只能被一个线程拥有
 61                 Owner 活动线程
 62                     初始时为NULL表示当前没有任何线程拥有该monitor record, 当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL
 63                 实现
 64                     同步代码块采用 monitorenter、monitorexit指令显示的同步
 65                     同步方法使用ACC_SYNCHRONIZED标记符隐式的实现
 66         锁优化
 67             自旋锁
 68                 该线程等待一段时间,不会被立即挂起,循环看持有锁的线程是否会很快释放锁
 69                 自旋数字难以控制(XX: preBlockSpin)
 70                 存在理论:线程的频繁挂起、唤醒负担较重,可以认为每个线程占有锁的时间很短,线程挂起再唤醒得不偿失。
 71                 缺点
 72                     自旋次数无法确定
 73             适应性自旋锁
 74                 自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定
 75                 自旋成功,则可以增加自旋次数,如果获取锁经常失败,那么自旋次数会减少
 76             锁消除
 77                 若不存在数据竞争的情况,JVM会消除锁机制
 78                 判断依据
 79                     变量逃逸
 80             锁粗化
 81                 将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。例如for循环内部获得锁
 82             轻量级锁
 83                 在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗
 84                 通过CAS(CompareandSwap),即比较并替换,来获取和释放锁
 85                 缺点
 86                     在多线程环境下,其运行效率比重量级锁还会慢
 87                 性能依据
 88                     对于绝大部分的锁,在整个生命周期内部都是不会存在竞争的
 89             偏向锁
 90                 为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径
 91                 主要尽可能避免不必要的CAS操作,如果竞争锁失败,则升级为轻量级锁
 92     原子操作
 93         处理器
 94             使用总线锁保证原子性
 95             使用缓存锁保证原子性
 96         JMM
 97  98                 JVM中除了偏向锁,其他锁(轻量级锁、互斥锁)的实现方式都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时候使用循环CAS释放锁。
 99                 JAVA1.6中,锁一共有4中状态,级别从低到高依次是:无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态
100             CAS
101                 Compare And Swap, 整个JUC体系最核心、最基础理论,Java中通过锁和CAS实现原子操作
102                 内存地址V,旧的预期值A,要更新的值B,当且仅当内存地址V中的值等于旧的预期值A时才会将内存值V得值修改为B,否则什么也不干
103                 native中存在四个参数
104                 JVM中的CAS操作利用了处理器提供的CMPXCHG指令实现的。
105                 缺陷
106                     ABA问题
107                         检查不到值的变化,实际上却变化了
108                         解决方案
109                             变量前追加版本号版本号
110                             AtomicStampedReference
111                                 这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子的方式将该引用和该标志的值设置为给定的更新值。
112                     循环时间太长
113                         自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销
114                         解决方法
115                             JVM如果能支持处理器提供的pause指令,那么效率一定会提升
116                             pause作用
117                                 1. 可以延迟流水线执行指令(depipeline),使CPU不会消耗过多的执行资源
118                                 2. 避免在退出循环的时候因内存顺序冲突而引起的CPU流水线被清空,从而提高CPU的执行效率
119                     只能保证一个共享变量原则操作
120                         对多个共享变量操作时,CAS无法保证操作的原子性,需要用锁
121                         解决方案
122                             AtomicReference类保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作
123                 原子操作类Atomic
124                     java.util.concurrent.atomic里的原子操作类提供了线程安全地更新一个变量的方式
125                     4大类型13个原子操作类
126                         基本类型类
127                             AtomicBoolean
128                             AtomicInteger
129                             AtomicLong
130                         数组
131                             AtomicIntegerArray
132                             AtomicLongArray
133                             AtomicReferenceArray
134                         引用
135                             AtomicReference
136                             AtomicReferenceFieldUpdater
137                             AtomicMarkableReference
138                         属性
139                             AtomicIntegerFieldUpdater
140                             AtomicLongFieldUpdater
141                             AtomicStampedReference
142                     核心底层
143                         CAS
144                             Unsafe只提供了3中CAS方法
145                                 final native boolean compareAndSwapObject()
146                                 final native boolean compareAndSwapInt()
147                                 final native boolean compareAndSwapLong()
148                 CAS V.S. 锁
149                     JVM中除了偏向锁,其他锁(轻量级锁、互斥锁)的实现方式都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时候使用循环CAS释放锁。
150 java 内存模型(JMM)
151     并发编程的挑战
152         线程上下文切换
153         死锁
154         资源限制的挑战
155     并发编程需要解决的两大问题
156         线程之间如何通信
157             线程通信机制
158                 内存共享
159                 消息传递
160         线程之间如何同步
161             同步是指程序中用于控制不同线间操作发生相对顺序的机制
162     内存模型
163         处理器内存模型
164             硬件级的内存模型
165             处理器的内存屏障
166                 内存屏障是一组处理器指令,用于实现对内存操作的顺序限制
167                 为了保证内存可见性,Java编译器再生产指令序列的适当位置会插入内存屏障制定来禁止特定类型的处理器重排序
168                 内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。
169                 4大类内存屏障指令
170                     LoadLoad Barriers
171                     LoadStore Barriers
172                     StoreStore Barriers
173                     StoreLoad Barriers
174                         全能型的屏障,同时具有其他三个屏障的效果,但执行该屏障开销会很大,因为Buffer Fully Flush
175         顺序一致性内存模型
176             多线程环境下的理想化的理论参考模型,处理器的内存模型和编程语言的内存模型都会以顺序一致性内存模型作为参照
177             为程序提供了极强的内存可见性保证
178             数据竞争
179                 定义
180                     在一个线程中写一个变量,在另一个线程中读同一个变量,而且写和读没有通过同步来排序
181                 当程序未正确同步时,就可能会存在数据竞争
182                 如果程序是正确同步的,程序的 执行将具有顺序一致性
183                 这里的同步是广义上的同步,包括常用同步原语的正确使用(Synchronized、volatile 、lock和 final184             特性
185                 一个线程中的所有操作必须按照程序的顺序来执行
186                 不管程序是否同步,所有程序都只能看到一个单一的操作执行顺序,每个操作都必须原子执行且立即对所有的线程可见
187         JMM
188             语言级的内存模型
189             JMM的内存可见性保证3方面
190                 基本方针:在不改变(正确同步的)程序执行结果的前提下,尽可能地为编译器和处理器的优化打开方便之门(重排序)
191                 1. 单线程程序。
192                 2. 正确同步的多线程程序。
193                 3. 未同步或未正确同步的多线程程序。
194             happens-before
195                 JMM中最核心的理论,保证跨线程的内存的可见性(可以在一个线程之内,也可以再不用线程之间)通过内存屏障实现
196                 背景
197                     JMM设计的两大核心目标
198                         为程序员提供足够强的内存可见性保证
199                         对编译器和处理器的约束尽可能放松(只要不改变程序的执行结果,编译器处理器怎么优化都行)首先保证正确性,然后再追求执行效率
200                 简单定义
201                     在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系
202                 定义
203                     JMM对程序员保证
204                         如果一个操作happens-before另一个操作,那么第一个操作的执行结果对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前
205                     JMM对编译器和处理器重排序的约束原则
206                         两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则判定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。
207                 原生java满足8大规则
208                     1. 程序顺序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
209                     2. 监视器锁规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
210                     3. volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
211                     4. 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
212                     5. 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
213                     6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
214                     7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
215                     8. 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
216                 happens-before与JMM的关系
217                     happens-before规则是JMM呈现给程序员的视图
218                     一个happens-before规则对应于一个或者多个编译器和处理器重排序规则
219                 happens-before VS as-if-serial
220                     as-if-serial 语义保证单线程内程序的执行结果不被改变;程序顺序执行
221                     happens-before 关系保证正确同步的多线程程序的执行结果不被改变;happens-before制定的顺序执行
222 java并发基础线程
223     线程的状态
224         6大状态
225             NEW
226                 初始状态,线程被构建,但是还没有调用start()方法
227             RUNNABLE
228                 运行状态,Java线程将操作系统中的就绪和运行两种状态统称为“运行状态”
229             BLOCK
230                 阻塞状态,表示线程阻塞于锁
231             WAITING
232                 等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或者等待)
233             TIME_WAITING
234                 超时等待状态,可以在等待的时间自行返回的
235             TERMINATED
236                 终止状态,表示当前线程已经执行完毕
237         Java状态转移
238             WAITING-->RUNNABLE
239                 Object.notify()
240                 Object.notifyAll()
241                 LockSupport.unpark(Thread)
242             TIME_WAITING-->RUNNABLE
243                 Object.notify()
244                 Object.notifyAll()
245                 LockSupport.unpark(Thread)
246             BLOCK-->RUNNABLE
247                 获取到锁
248             1. 实例化后New
249             2. New-->RUNNABLE
250                 系统调度
251                 Thread.start()
252                     running-->ready
253                         Thread.yield
254                     ready-->running
255             3. RUNNABLE-->WAITING
256                 Object.wait()
257                 Thread.join()
258                 LockSupport.park()
259             4. RUNNABLE-->TIME_WAITING
260                 Object.wait(long)
261                 Thread.sleep(long)
262                 Thread.join(long)
263                 LockSupport.parkNanos()
264                 LockSupport.parkUntil()
265             5. RUNNABLE-->BLOCKED
266                 等待进入synchronized方法
267                 等待进入synchronized块
268             6. RUNNABLE-->TERMINATED
269             Java 线程状态变迁
270             yield
271                 暂停当前正在执行的线程对象。
272                 yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
273                 yield()只能使同优先级或更高优先级的线程有执行的机会。 
274                 注意:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
275         注意
276             等待进入synchronized方法/块 为阻塞状态
277             java.concurrent.Lock为   等待状态,因为Lock接口对于阻塞的实现使用了LockSupport类中的相关方法
278     程序 VS 进程 VS 线程
279         程序
280             一组指令的有序结合,是静态的指令,是永久存在的
281         进程
282             具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源(打开的文件,创建的socket)分配和调度的一个独立单元。进程的存在是暂时的,是一个动态的概念。
283         线程
284             线程是CPU调度和分配的基本单位,是比进程更小的能独立运行的基本单元。本身基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈)。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
285         进程 VS 线程
286             定义
287                 进程是资源分配和调度的基本单位;线程是CPU/任务调度和执行的基本单位
288             包含关系
289                 1个进程包含有多(大于等于1)个线程; 线程是进程的一部分(轻量级进程)
290             地址空间
291                 进程之间地址空间独立; 同一进程内的线程共享本进程的地址空间
292             切换开销
293                 进程之间切换开销大; 线程之间切换开销小(创建和销毁)
294             创建
295                 进程fork/vfork;    线程pthread_create
296             销毁
297                 进程结束,它拥有的所有线程都将销毁; 线程结束,不会影响同个进程中的其他线程
298             私有属性
299                 进程:PCB(进程控制块);  线程:TCB(线程控制块),线程Id,寄存器,上下文
300         一个程序至少只有一个进程,一个进程至少有一个线程
301     启动和终止线程
302     线程间通信
303         volatile & synchronized
304             共享内存和本地内存拷贝的同步更新问题,使得变量不一定能是最新的
305             volatile: 保证所有线程对变量的可见性
306             synchronized:保证线程对变量访问的可见性和排他性,获取monitor; 主要确保多个线程在同一时刻,智能有一个线程处于方法或者同步块中。
307         等待/通知机制
308             任意java对象所具备的
309             依托于同步机制,目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量作出的修改
310             等待通知机制:线程A调用了对象O的wait()方法进入了等待状态,而线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象的wait()与notify()或notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
311         等待超时模式
312             等待超时模式就是在等待/通知范式基础上增加了超时控制,避免执行时间过长,也不会“永久”阻塞调用者,而是按照调用者的要求返回。
313             超时等待:调用一个方法时,等待一段时间(一般给定一个时间段),如果该方法能够在给定的时间段内得到结果,那么将结果立刻返回,反之,超时返回默认结果。
314         管道输入/输出流
315             管道输入输出流与普通的文件输入输出流或者网络输入输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介是内存
316             4种具体的实现
317                 PipedOutputStream
318                 PipedInputStream
319                 PipedReader
320                 PipedWriter
321                 面向字节
322                 面向字符
323         Thread.join()的使用
324             涉及了等待/通知机制,等待前驱线程结束,接收前驱线程结束通知,源码本质也是wait()和notifyAll()
325         ThreadLocal的使用
326             ThreadLocal,即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
327             ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。作用:提供一个线程内公共变量,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
328             方法
329                 initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法。如果set()方法没有调用,第一次get()方法调用时会进行初始化 initialValue(),每个线程会调用一次。
330                 get()方法是用来获取ThreadLocal在当前线程中保存的变量副本
331                 set(T value)用来设置当前线程中变量的副本
332                 remove()用来移除当前线程中变量的副本
333             工作原理
334                 Thread类中有一个成员变量属于ThreadLocalMap类(一个定义在ThreadLocal类中的内部类),它是一个Map,他的key是ThreadLocal实例对象。
335                 当为ThreadLocal类的对象set值时,首先获得当前线程的ThreadLocalMap类属性,然后以ThreadLocal类的对象为key,设定value,值时则类似。
336                 ThreadLocal变量的活动范围为某线程,是该线程“专有的,独自霸占”的,对该变量的所有操作均由该线程完成!也就是说,ThreadLocal 不是用来解决共享对象的多线程访问的竞争问题的,因为ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。当线程终止后,这些值会作为垃圾回收。
337                 由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非是共享的,
338             存储结构的好处
339                 1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。
340                 2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
341             弱引用GC
342                 1、使用完线程共享变量后,显示调用remove方法清除线程共享变量可以及时清除
343                 2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
344                 3、对于ThreadLocal变量,我们可以手动的将其置为Null,比如tl =null。那么这个ThreadLocal对应的所有线程的局部变量都有可能被回收。
345             解决Hash冲突方法
346                 线性探测
347             应用场景: 用来解决 数据库连接、Session管理、AOP耗时统计等。
348             注意
349                 不过有点遗憾的是只能放一个值,再次调用set设置值,会覆盖前一次set的值。如果要多个变量,新建多个ThreadLocal对象
350                 是单个线程内函数和组件的共享变量,不是多线程的共享变量,线程隔离
351 352     Lock接口
353         锁是用来控制多个线程访问共享资源的方式。一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁)。
354         在Lock接口出现前,java使用synchronized关键字实现锁的功能,但是在javaSE5之后,并发包中提供了Lock接口(以及其实现类)用来实现锁的功能。
355         Lock V.S. synchronized 
356             Lock接口有而synchronized不具备的主要特性
357                 尝试非阻塞地获取锁
358                     当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁
359                 超时获取锁
360                     在指定的截止时间之前获取锁,如果截止时间到了仍旧无法获取锁,则返回
361                 能被中断地获取锁
362                     与synchronized不同,获取到所得线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
363             Lock提供了与synchronized相似的功能,但必须显示的获取锁与释放锁,虽然不及隐式操作方便,但是拥有了锁获取与释放的可操作性、可中断的锁获取与超时获取锁等多重功能。
364     队列同步器AQS
365         队列同步器AbstractQueuedSynchronizer(以下简称同步器), 是用来构建锁或者其他同步组件(继承lock)的基础框架,
366             它使用了一个int成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
367             同步框架,提供通用机制来原子性管理同步状态、阻塞和唤醒线程,以及维护被阻塞线程的队列
368             基于AQS实现的同步器包括:ReentrantLock、Semaphore、ReentrantReadWriteLock、CountDownLatch和FutureTask(任务)
369         使用方式
370             继承
371                 同步器的主要使用方法是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。
372             静态内部类
373                 子类被推荐定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放方法来供自定义同步组件使用
374             独占式共享式获取同步状态
375                 同步器即可以支持独占式获取同步状态,也可以支持共享式地获取同步状态,这样方便实现不同类型的同步组件(ReentrantLock、ReentrantReadWriteLock、CountDownLatch等)。
376         锁 VS 同步器
377             联系
378                 同步器是实现锁(也可以是任何同步组件)的关键:在锁中聚合同步器,利用同步器实现锁的语义。lock方法内部调用实现了AQS的内部类的require方法
379             区别
380                 锁是面向使用者的,他定义了使用者与锁交互的接口(比如允许两个线程并行访问),隐藏了实现细节;
381                 同步器是面向锁的实现者,它简化了锁的实现方式,屏蔽了同步管理状态、线程的排队、等待与唤醒等底层操作。
382                 锁让使用者仅仅是调用其方法既可以实现同步效果、同步器让实现者通过重写抽象方法进行了队列的底层操作。他们两个是使用者和实现者关注不同的领域实现了相同的效果。
383         API
384             AbstractQueuedSynchronizer,同步器,实现JUC核心基础组件
385             同步器基于模板设计模式实现的,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义的同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法会调用使用者重写的方法。
386             自定义同步组件(extends Lock)将使用同步器 (静态内部类) 提供的模板方法来实现自己的同步语义
387     重入锁
388         ReentrantLock
389             可重入锁,是一种递归无阻塞的同步机制
390             比synchronized更强大,灵活的锁机制,可以减少死锁发生的概率
391             分为公平锁和非公平锁
392             底层采用AQS实现,通过内部Sync继承AQS
393     读写锁
394         ReentrantReadWriteLock
395             读写锁,两把锁:共享锁-读锁、排它锁-写锁
396             支持公平性、非公平性,可重入和锁降级
397             锁降级:遵循获取写锁、获取读锁在释放写锁的次序,写锁可以降级成为读锁。
398     LockSupport工具
399         阻塞和唤醒一个线程
400     Condition接口
401         Lock提供条件Condition,对线程的等待、唤醒操作更加详细和灵活,依赖于lock
402         Lock提供条件Condition,对线程的等待、唤醒操作更加详细和灵活
403         内部维护一个Condition队列,当前线程调用await()方法,将会以当前线程构造一个节点(Node),并将节点加入到该队列的尾部。
404 java 并发容器
405     ConcurrentHashMap
406         并发编程中需要用到线程安全的HashMap
407         1.8与1.7的区别
408             数据结构
409                 JDK 1.7 数组+链表:Segment(ReentrantLock)+HashEntry 
410           JDK 1.8 数组+链表/红黑树 Node(HashEntry) + Synchronized+CAS+红黑树 
411             线程安全
412                 JDK 1.7 Segment(ReentrantLock)
413           JDK 1.8 Synchronized+CAS
414             锁粒度
415                 JDK1.7  锁的粒度是基于Segment的,包含多个HashEntry
416           JDK1.8  锁的粒度是基于Node的(HashEntry首节点),实现降低锁的粒度
417             查询时间复杂度
418                 JDK1.7 遍历链表 O(N)
419           JDK1.8 遍历红黑树 O(log N)
420                 链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。
421             辅助类
422                 JDK 1.8 例如TreeBin,Traverser等对象内部类。
423     ConcurrentLinkedQueue
424         并发编程中需要用到线程安全的队列
425             1. 使用阻塞算法
426                 1个锁(入队和出队用同一把锁)和2个锁(入队和出队用不同的锁)
427                     阻塞队列
428             2. 使用非阻塞算法
429                 CAS
430                     ConcurrentLinkedQueue
431         基于链接节点的无边界的线程安全队列,采用FIFO原则对元素进行排序,内部采用CAS算法实现
432         精妙之处:利用CAS来完成数据操作,同时允许队列的不一致性,弱一致性表现淋漓尽致。
433     ConcurrentSkipListMap
434         ConcurrentSkipListMap其内部采用SkipLis数据结构实现。
435     ConcurrentSkipListSet
436         内部采用ConcurrentSkipListMap实现
437 Java阻塞队列(7个)
438     阻塞队列是一个支持两个附加操作的队列
439     两个附加操作
440         支持阻塞的插入方法
441             当队列满时,队列会阻塞插入元素的线程,直到队列不满
442         支持阻塞的移除方法
443             当队列空时,队列会阻塞获取元素的线程,直到队列非空
444     实现原理
445         使用通知模式
446         当生产者往满的队列里添加元素时会阻塞住生产者,当消费者消费了一个队列中的元素后,会通知生产者当前队列可用
447     一个抽象类
448         AbstractQueue,改类在Queue接口中扮演着非常重要的作用,该类提供了对queue操作的骨干实现
449     一个接口
450         BlockingQueue继承java.util.Queue为阻塞队列的核心接口,提供了在多线程环境下的出列、入列操作,作为使用者,则不需要关心队列在什么时候阻塞线程,什么时候唤醒线程,所有一切均由BlockingQueue来完成。
451     七个阻塞队列
452         ArrayBlockingQueue
453             一个由数组组成的有界阻塞队列
454         LinkedBlockingQueue
455             一个由链表组成的有界阻塞队列
456         LinkedBlockingDeque
457             一个由链表组成的双向阻塞队列
458         PriorityBlockingQueue
459             一个支持优先级排序的无界阻塞队列
460         DelayQueue
461             一个使用优先级队列实现的无界阻塞队列
462         SynchronousQueue
463             一个不存储元素的阻塞队列
464         LinkedTransferQueue
465             一个由链表组成的无界阻塞队列
466                 即可以像其他的BlockingQueue一样有容量又可以像SynchronousQueue一样不会锁住整个队列
467         有界
468         对读或者写都是锁上整个队列,在并发量大的时候,各种锁是比较耗资源和耗时间的
469     阻塞队列的4中处理方式
470         抛出异常
471         返回特殊值
472         一直阻塞
473         超时退出
474         注意: 如果是无界阻塞队列, 队列不可能出现满的情况,使用put或offer方法永远不会被阻塞,而且使用offer方法时,永远返回true
475 java 并发工具(4个)
476     CountDownLatch
477         等ABCD 4个人都结束了,自己才能开始,结束一个减一个
478         CountDownLatch 它允许一个或多个线程等待其他N个指定数量线程完成操作
479         CountDownLatch也可以实现join的功能,并且比join的功能更多
480         AQS(队列同步器) 共享锁
481         await()
482     CyclicBarrier
483         我和A,B,C,D 5个人互相等待,会合了再一起进电影院,到一个减一个
484         定义
485             可循环使用的同步屏障:它允许一组线程互相等待,直到达到某个公共屏障点(common barrier point)
486             通俗讲:让一组线程到达一个屏障是被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活
487         底层采用ReentrantLock + Condition 实现
488         应用场景
489             多线程结果合并的操作,用于多线程计算数据,最后合并计算结果的应用场景
490     Semaphore
491         计数器,鸡蛋篮子里只能放5个鸡蛋,缺几个,才能放几个
492         信号量
493             计数器,用来控制同时访问特定资源的线程数量,他通过协调各个线程,以保证合理的使用公共资源
494         内部采用共享锁实现
495         从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对许可的号码进行计数,并采取相应的行动。
496         应用场景
497             用于流量控制,特别是公用资源有限的应用场景,比如数据库连接。
498     Exchanger
499         A和B交换数据
500         定义
501             交换者,是一个用于线程间协作的工具类,用于进行线程间的数据交换,两个线程
502             它提供一个同步点,用于进行线程间成对配对及交换数据,
503         具体
504             第一个线程先执行exchange()方法,它会一只等待第二个线程也执行exchange()方法,当两个线程都到达同步点时,两个线程就可以交换数据,将本线程生产出来的数据传递给对方
505         允许在并发任务之间交换数据。具体来说,Exchanger类允许在两个线程之间定义同步点。当两个线程都到达同步点时,他们交换数据结构,因此第一个线程的数据结构进入到第二个线程中,第二个线程的数据结构进入到第一个线程中
506         应用场景
507             遗传算法
508                 交叉
509             校对工作
510                 AB岗录入电子银行流水
511             Exchanger 可能被视为 SynchronousQueue 的双向形式。Exchanger 可能在应用程序(比如遗传算法和管道设计)中很有用。
512 java 原子操作类Atomic(13个)
513     java.util.concurrent.atomic里的原子操作类提供了线程安全地更新一个变量的方式
514     4大类型13个原子操作类
515         基本类型类
516             AtomicBoolean
517                 原子更新布尔类型
518             AtomicInteger
519                 原子更新整型
520             AtomicLong
521                 原子更新长整型
522             用于通过原子的方式更新基本类型
523         数组
524             AtomicIntegerArray
525                 原子更新整形数组里的元素
526             AtomicLongArray
527                 原子更新长整型数组里的元素
528             AtomicReferenceArray
529                 原子更新引用类型数组里的元素
530             通过原子的方式更新数组里的某个元素
531         引用
532             AtomicReference
533                 原子更新引用类型
534             AtomicReferenceFieldUpdater
535                 原子更新引用类型里的字段
536             AtomicMarkableReference
537                 原子更新带有标记位的引用类型
538             如果要原子地更新多个变量,就需要使用这个原子更新引用类型提供的类
539         属性
540             AtomicIntegerFieldUpdater
541                 原子更新整型的字段的更新器
542             AtomicLongFieldUpdater
543                 原子更新长整型的字段的更新器
544             AtomicStampedReference
545                 原子更新带有版本号的引用类型
546             如果需要某各类的某个字段,使用原子更新字段类
547     核心底层
548         CAS
549             Unsafe只提供了3中CAS方法
550                 final native boolean compareAndSwapObject()
551                 final native boolean compareAndSwapInt()
552                 final native boolean compareAndSwapLong()
553 java并发框架(2个)
554     Fork/Join框架
555         定义
556             一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架
557          核心思想
558              分治
559              fork分解任务,join收集任务
560          工作窃取算法
561             定义
562                  工作窃取算法work-stealing: 某个线程从其他队列里窃取任务来执行
563             背景
564                 将一个不较大的任务分割为若干个互不依赖的子任务,为了减少线程之间的竞争,把这些子任务分别放到不同的队列里,并未每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。
565                  执行快的线程帮助执行慢的线程执行任务,提升整个任务效率
566                 为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列
567 窃取任务线程永远从双端队列的尾部拿任务执行
568 被窃取任务线程永远从双端队列的头部拿任务执行
569             优点
570                 充分利用线程进行并行计算,减少了线程间的竞争
571             缺点
572                 在某些情况下还是存在竞争,比如双端队列里只有一个任务时,并且该算法会消耗了更多的资源,比如创建多个线程和多个双端队列
573         核心类
574             ForkJoinTask
575                 子类,用于继承
576                     继承子类 RecursiveAction
577                         用于没有返回结果的任务
578                     继承子类 RecursiveTask
579                         用于有返回结果的任务
580                 方法
581                     fork
582                         分解任务
583                     join
584                         合并任务结果
585                     isCompletedAbnormally()
586                         检查任务是否已经抛出异常或已经被取消了
587                     getException()
588                         获取异常
589              ForkJoinWorkerThread
590                  执行任务的工作线程
591             ForkJoinPool
592                  执行任务ForkJoinTask的线程池,ForkJoinTask需要通过ForkJoinPool来执行
593                     submit(task);
594                 内部结构
595                     ForkJoinTask数组
596                         存放任务
597                     ForkJoinWorkerThread数组
598                         执行任务
599     Executor框架
600         Executor框架的结构
601             任务
602                 包括被执行任务需要实现的接口
603                     Runnable接口
604                     Callable接口
605             任务的执行
606                 任务执行机制的核心接口
607                     Executor接口
608                 继承自Executor的接口
609                     ExecutorService接口
610                         execute(Runnable command)
611                         submit(Runnable task)
612                         submit(Callable<T>task)
613                         返回值是FutureTask对象
614                 实现了ExecutorService接口的实现类
615                     ThreadPoolExecutor
616                     ScheduledThreadPoolExecutor
617             异步计算的结果
618                 Future接口
619                     get
620                         等待任务执行完成
621                     cancel
622                         取消任务完成
623                 实现了Future接口的实现类
624                     FutureTask
625         ThreadPoolExecutor(线程池)
626             是线程池的核心实现类,用来执行被提交的任务
627             ThreadPoolExecutor
628                 corePoolSize
629                     线程池中核心线程的数量。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线程数等于corePoolSize。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
630                 maximumPoolSize
631                     线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程数小于maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就没有什么效果了。
632                 keepAliveTime
633                     线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。
634                 unit
635                     keepAliveTime的单位。TimeUnit
636                 workQueue
637                     用来保存等待执行的任务的阻塞队列,等待的任务必须实现Runnable接口。我们可以选择如下几种:
638                     分类
639                         ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。
640                         LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
641                         SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作,反之亦然。
642                         PriorityBlockingQueue:具有优先界别的无界阻塞队列。
643                 threadFactory
644                     用于设置创建线程的工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字,该对象可以通过Executors.defaultThreadFactory()
645                 handler
646                     RejectedExecutionHandler,线程池的拒绝策略。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时,如果此时线程池中的线程已经饱和了,而且阻塞队列也已经满了,则线程池会选择一种拒绝策略来处理该任务。
647                     四种拒绝策略
648                         AbortPolicy:直接抛出异常,默认策略;
649                         CallerRunsPolicy:用调用者所在的线程来执行任务;
650                         DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
651                         DiscardPolicy:直接丢弃任务;
652                     当然我们也可以实现自己的拒绝策略,例如记录日志、持久化存储不能处理的任务等等,实现RejectedExecutionHandler接口自定义即可。
653             工厂类Executors创建3种类型的ThreadPoolExecutor
654                 SingleThreadExecutor
655                     使用单个worker线程的Executor
656                     应用场景
657                         适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景
658                     特点
659                         corePool和maximumPoolSize均被设置为1
660                         使用的是相当于无界的有界阻塞队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样。
661                 FixedThreadPool
662                     固定线程数的线程池
663                     应用场景
664                         为了满足资源管理的需求,需要限制当前线程数量的应用场景
665                         适用于负载比较重的服务器
666                     特点
667                         corePoolSize 和 maximumPoolSize都设置为创建FixedThreadPool时指定的参数nThreads,意味着当线程池满时且阻塞队列也已经满时,如果继续提交任务,则会直接走拒绝策略
668                         默认的拒绝策略,即AbortPolicy,则直接抛出异常。
669                         keepAliveTime设置为0L,表示空闲的线程会立刻终止。
670                         workQueue则是使用LinkedBlockingQueue,但是没有设置范围,那么则是最大值(Integer.MAX_VALUE),这基本就相当于一个无界队列了。
671                         无界队列对线程池的影响
672                             1. 当线程池中的线程数量等于corePoolSize 时,如果继续提交任务,该任务会被添加到无界阻塞队列workQueue中,因此线程中的线程数不会超过corePoolSize
673                             2. 由于1,使用无界队列时的 maximumPoolSize是一个无效参数
674                             3. 由于1和2,使用无界队列时的 keepAliveTime 是一个无效参数
675                             4.  不会拒绝任务
676                 CachedThreadPool
677                     根据需要创建新线程,是大小无界的线程池
678                     应用场景
679                         适用于执行很多的短期异步任务的小程序
680                         适用于负载较轻的服务器
681                     特点
682                         corePool为0,maximumPoolSize为Integer.MAX_VALUE,这就意味着所有的任务一提交就会加入到阻塞队列中。
683                         keepAliveTime这是为60L,unit设置为TimeUnit.SECONDS,意味着空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。
684                         阻塞队列采用的SynchronousQueue,每个插入操作都必须等待另一个线程对应的移除操作,此处把主线程提交的任务传递给空闲线程去执行。
685                         SynchronousQueue是一个没有元素的阻塞队列,加上corePool = 0 ,maximumPoolSize = Integer.MAX_VALUE,这样就会存在一个问题,如果主线程提交任务的速度远远大于CachedThreadPool的处理速度,则CachedThreadPool会不断地创建新线程来执行任务,这样有可能会导致系统耗尽CPU和内存资源,所以在使用该线程池是,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。
686                     重要操作
687                         offer
688                             主线程执行offer操作与空闲线程执行的poll操作配对成功后,主线程把任务交给空闲线程执行
689                         execute
690                             执行任务
691                         poll
692                             让空闲线程在SynchronousQueue中等待60s,如果等待到新任务则执行,否则,空闲线程将终止
693         ScheduledThreadPoolExecutor
694             可以在给定的延迟后运行命令,或者定期执行命令,与Timer类似,但比其功能更强大,更灵活
695         FutureTask
696             实现了Future接口和Runnable接口,代表异步计算的结果,
697             应用场景
698                 当一个线程需要等待另一个线程把某个任务执行完后它才能继续执行
View Code

 

11. 参考网址:

  1. 参考来源:http://cmsblogs.com/wp-content/resources/img/sike-juc.png
  2. 《Java并发编程的艺术》_方腾飞PDF 提取码:o9vr
  3. http://ifeve.com/the-art-of-java-concurrency-program-1/
  4. Java并发学习系列-绪论
  5. Java并发编程实战
  6. 死磕 Java 并发精品合集

 

posted @ 2019-07-17 11:30  海米傻傻  阅读(1131)  评论(2编辑  收藏  举报