java并发复习笔记总结

本文地址:http://www.cnblogs.com/maplefighting/p/7941885.html html

一、volatile:轻量级的synchronized,不会引发线程上下问切换java

为了提升速度,处理器不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存后再操做。声明了volatile,jvm向处理器大宋Lock前缀指令,将变量在缓存行的数据写到系统内存,每一个处理器经过总线传播的数据检查本身缓存的值是否过时。node

二、volatile实现原则:算法

(1) Lock前缀指令会引发处理器缓存回写到内存。数据库

(2) 一个处理器的缓存回写到内存会致使其余处理器的缓存失效。编程

三、锁的状态,级别从低到高为:无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态数组

      锁能够升级但不能降级缓存

      偏向锁:只有一个线程进入临界区安全

      轻量级锁:多个线程交替进入临界区     CAS数据结构

      重量级锁:多个线程同时进入临界区

四、处理器实现原子操做:(1) 总线锁    (2) 缓存锁定

五、Java实现原子操做:(1) 锁      (2) 循环CAS

六、volatile:写happens-before 读

写一个volatile变量时,JMM (Java内存模型) 会把该线程对应的本地内存中的共享变量刷新到主内存中。

读一个volatile变量时,JMM会把本地内存置无效,从内存读取共享变量。

七、CAS:先操做比较与预期的值是否同样,同样就设置,不同就继续循环(CompareAndSet)

      CAS同时具备volatile读与写到内存语义

八、happens-before 先行规则

(1) 程序顺序规则:一个线程的每一个操做happens-before于该线程中的任意后序操做

(2) 监视器锁规则:对一个锁的解锁happens-before于任意后序对这个锁的加锁

(3) volatole变量规则:对一个volatile域的写happens-brfore于读

(4) 传递性:A happens-before B,B happens-before C => A happens-before C

(5) start()规则:若是A执行 ThreadB.start(),那么 A线程的ThreadB.start()  happens-before B的任意操做

(6) join()规则:若是A执行ThreadB.join()并成功返回,那么B中任意操做happens-before 于A从 ThreadB,join()操做成功返回

九、上下文切换:任务从保存到加载的过程

减小上下文切换有无锁并发编程,CAS算法,使用最少线程和使用协程

十、Java内存模型

线程之间的共享变量存储在主内存中,每一个内存都有一个私有的本地内存,本地内存存储了该线程读写共享变量的副本,本地内存只是一抽象,并不真实存在。

十一、final 内存语义

(1) 在构造函数内对final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,不能重排序。

(2) 初次读一个包含final域对象的引用,与随后初次读这个fianl域,不能重排序。

 十二、as-if-serial 语义保证单线程内程序的执行结果不被改变

        happens-before 保证正确同步的多线程程序执行结果不被改变

1三、HashMap 

jdk1.7多线程put会死循环,由于Entry链表造成环数据结构,Entry的next永远不为空

没使用Synchronized,可接受 null 的 key 和 value

&   默认容量为16,扩容2倍     自定义的hash

HashTable 

使用 Synchronized,key 和value 都不能 null

%   默认容量为11,扩容两倍+1     hashcode

ConcurrentHashMap

段分锁,提升并发访问效率

segment->可重入锁,2^n长度  包含HashEntry的链表结构

1四、ConcurrentHashMap

(1) 由 Segment 和 HashEntry 构成   , Segment长度为2的n次方,jdk1.7 Segment会锁住多个HashEntry

(2) get:先通过一次再散列,根据值使用散列运算定位到Segment,再经过散列算法定位到元素。get里面的共享变量都定义为volatile类型,因此不用加锁

(3)定位HashEntry 和定位Segment 都是与数组长度-1 相与,可是相与的"值"不同。Segment使用的是元素的hashcode 经过再散列后的值的高位,定位HashEntry 直接使用再散列后的值。目的:避免两次值同样

(4) put 必须加锁,定位到Segment ,判断是否HashEntry 要扩容,而后再添加

(5) 第一次put时才初始化

1五、ConcurrentHashMap

jdk1.8改进:(1)取消Segment字段,使用volatile HashEntry<K,V>[] 数组元素做为锁         (2) 改成table数组 + 单向链表 + 红黑树,节点超过8会用红黑树

jdk1.7

put:(1) A tryLock 成功得到锁,把hashEntry插入

(2)获取锁失败,执行scanAndLockForPut方法,重复执行tryLock,多处理器重复64,单处理器重复1次,超过期,挂起线程

size:(1)不加锁,计算两次,相同说明准确。不同,给每一个Segment加锁,再计算

modCount    put,remove,clean时会加1

jdk1.8

size:元素个数保存在baseCount,部分元素的变化个数保存在CounterCell数组中,累加

 a、新增节点后,链表的元素个数达到8,就转换成红黑树,不过转换以前,若是数组长度小于阀值,默认为64 (数组长度大于64,才会考虑转换红黑树),则会扩大两倍,并触发transfer

b、元素个数0.75倍数组,扩容 (hashMap也同样)

1六、ReentrantLock 可重入锁,分为公平锁和非公平锁

a、公平锁:队列顺序,等待久的先

      非公平锁:能够抢占   CAS

b、公平锁和非公平锁,释放最后都要写一个volatile变量

      公平锁,获取时会先读volatile变量。非公平锁获取时,会用CAS更新volatile变量

c、非公平锁用CAS。  公平锁加个判读去年当前节点是否有前驱节点 hasQueuedPredecessors()方法

d、非公平锁可能形成线程"饥饿",可是极少的线程切换,保证吞吐量,吞吐量更大。

1七、ReetrantReadWriteLock读写锁

高16位读,低16位写  AQS           可重入

锁降级:把持当前拥有的写锁,再获取读锁,稍后释放锁。目的:保证数据的可见性

1八、队列同步器 AQS (AbstractQueuedSynchronizer)  构建锁和或其余同步组件的基础架构   ReetrantLock,Condition,ReetrantReadWriteLock等

同步器提供  getState(),setState( .... ),conpareAndSetState( .... )

重写AQS时,可使用CAS等

实现:a、同步队列  双向链表  ,CAS设置尾节点

   b、独占式同步状态获取与释放   CAS入队enq(node),出队时,每一个node都在自旋

   c、共享式 <-- 释放同步线程CAS

内部使用volatile修饰 int state 表示同步状态

共享式同步状态:tryAcquireShared(arg) >= 0时,能得到同步状态

获取同步状态失败进入同步队列(addWaiter),先CAS设置,失败进入enq方法。出队列不须要CAS

1九、ConcurrentLinkedQueue   不要用size(),会遍历所有

(1)非阻塞,入队永远返回true,用CAS

不是每次节点入队后都将tail节点更新为尾节点,而是当tail与尾节点距离>=HOPS常量时才更新,提升入队效率

出队仅没有元素时才会更新head节点,减小CAS

(2)阻塞队列

插入:队列满时阻塞       移除:队列空时阻塞

使用通知模式  condition   消费者消费通知生产者

阻塞生产者经过LockSupport.park实现

20、Condition接口    是AQS的内部类

condition定义了await()和signal(),signalAll() 方法

获取一个condition要经过Lock的newCondition。 

一个condition包含一个等待队列,增长节点没有CAS保证,由await()锁保证线程安全

(1) await()时会使当前线程从同步队列首节点构形成一个新节点,加入等待队列中。

(2) 调用condition的signal() 方法时,将等待队列中首节点加到同步队列中

2一、LockSupport 每一个线程都有一个Park实例, unPark能够先于Park出现

基于Unsafe实现的

使用Park使线程挂起,释放(1) 其余线程调用unPark,(2) 线程中断,(3) park方法当即返回

2二、Fork/Join框架:分割任务,执行并合并结果。    工做窃取算法

2三、CycleBarrier 全部线程彼此等待    CountDownLatch:只要报到

容许一个或多个线程等待其余线程完成操做

CountDownLatch的计数器只使用一次

CycleBarrier能够reset(),跟join差很少(AQS,ReetrantLock)

等多有线程都运行到下一个步骤前等待

2四、控制并发线程数的Semaphore (信号量) AQS

用来控制同时访问特定资源的线程数量

2五、Synchronize和Lock的区别

(1) Lock的锁是Java写的控制锁的代码。  Synchronize是托管给jvm的,Java关键字

(2) Synchronize在异常时,jvm会释放锁。  Lock不会主动释放锁,要手工释放。

(3) Synchronize 悲观的排他锁。    Lock有读写锁,公平锁,非公平锁

2六、线程池 ThreadPoolExecutor

好处:(1) 下降资源消耗  (2) 提升响应速度  (3) 提升线程可管理性

流程:a、判断核心线程池线程是否执行任务。  建立线程须要获取全局锁

    b、是的话,判断工做队列是否已满。   目的:尽量避免获取全局锁

    c、是的话,判断线程池的线程是否都处于工做状态

    c满了就调用饱和策略。默认抛出异常

提交任务:execute()  不返回值     submit() 返回future类型的对象

关闭池:shutdownNow  中止全部     shutdown 中断没有执行任务的线程

线程池最大容量   AtomicInteger类型,capacity前三位为标志位,因此最大为(2^29) - 1

状态分别为:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED

参数:corePoolSize 核心线程数     maximumPoolSize 最大线程数    keepAliveTime 最大存活时间    rejectExecutionHandler 任务拒绝处理策略

2七、Executoe框架

应用框架经过Executor框架控制上层调度,而下层调度由操做系统内核控制

使用流程:主线程先建立Runnable或Callable接口,而后交给ExecutorService执行,返回Future接口对象

2八、ThreadLocal

一个线程能够根据ThreadLocal查询到绑定在这个线程的一个值,可用于数据库链接

提供get和set访问与当前相关的局部变量。变量是保存在线程中的threadLocals (threadLocalMap类型的)

ThradLocalMap.Entry弱引用,随时可能被回收

get先获得当前线程的ThreadLocalMap,再根据 ××.Entry = get(this) 获得值

2九、Synchroinzed:jvm基于进入与退出使用monitorenter和monitorexit指令实现,偏向锁,轻量级锁

30、atomic×××

value成员都是volatile   CAS

基本方法:get/set,compareAndSet  (unsafe.compareAnsSwapInt(....) )

CAS:调用UnSafe的方法,不是用Java实现,而是JNI调用操做系统原生程序

3一、CopyOnWriteArrayList  占用内存

当咱们往一个容器添加元素时,不直接往当前容器添加,而是先copy复制一个新的容器,添加完后,再将原容器指向新的。

并发读不用加锁。添加时要加锁,不然多线程会copy出多个副本。

若是在最后添加元素,则用Array.copyOf()。若是在中间插入,则用System.arraycopy分两段复制。

应用读多写少并发场景,如白名单,黑名单

 

参考书籍:Java并发编程的艺术(推荐),juc包源码

--------------------------------------------------------------------------------------------------------------

以上为maplefighting我的笔记整理,若有出错,欢迎指正

相关文章
相关标签/搜索