# javacp **Repository Path**: fuckjava/javacp ## Basic Information - **Project Name**: javacp - **Description**: java并发编程示例代码(Java Concurrent Programming) - **Primary Language**: Java - **License**: AFL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-04-12 - **Last Updated**: 2021-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 1. 线程基础 、线程之间的共享和协作 #### 基础概念 1. 什么是进程和线程 2. CPU核心数和线程数的关系 3. CPU时间片轮转机制 4. 澄清并行和并发 5. 高并发编程的意义、好处和注意事项 1. 可以充分利用CPU的资源 2. 加快响应用户的时间(如电商把发短信邮件让另一个线程去执行) 3.线程安全 1. 产生死锁 2. 产生锁的竞争,导致程序性能下降 3. 操作系统限制,Linux一个进程最多只能有1000个进程,windows 2000个 每个线程分配栈空间 1M 1G 句柄 文件描述符 1024 #### Java中的线程 >java程序天生就是多线程,有2种启动线程的方式 1. 类Thread 2. 接口Runnable >Thread 和Runnable的区别 1. Thread是对线程的抽象 2. Runnable对任务的抽象 >怎样才能让线程安全的停止工作呢? * stop():不建议使用,强制性,不管你有没有释放资源 * interrupt():对线程进行中断,只是打招呼,不会立即中断,甚至不用理会 * isInterrupted():判断当前线程是否被中断 * interrupted():判断线程是否被中断,会改变线程状态标志位,true 改为false * 实现Runnable接口的类,同个Thread.currentThread().isInterrupted()来控制线程 jdk的线程是协作式的,不是 >线程常用方法和线程状态 执行run方法的是主线程 执行start方法的是线程本身 run只能执行一次,start运行几次执行几次 yield()方法将线程从运行转到可运行状态 ![94f3cca041ea9ccfa7b916f772e42bfd.png](en-resource://database/3284:1) >yield() 时间片到期,让当前的线程让出CPU的占有权 1. 让出的时间不可设定 2. 让出的锁资源不会主动释放 >join(): 获取执行权 怎么让两个线程按顺序执行?答:使用join()方法(串行) 线程的优先级 1. 1-10级,默认5,优先级能不能保证,取决于操作系统 守护线程 手动启动的,用户线程,非守护 jdk内部启动的,守护线程,用户线程结束,守护线程才结束 自己想用守护线程,useThread.setDaemon(true) 守护线程中,finally不一定起作用 >什么是线程间的共享 synchronized内置锁(锁的是对象) * 用处和用法 * 同步块 * 方法 * 对象锁 * 类锁 备注: 1. 基本类型不是对象,synchronized不能锁它 >volatile关键字,最轻量的同步机制 1. 可见性,但是不能保证原子,适合一写多读(一个线程写)的场景 >错误的加锁以及原因分析 (i++;每次对象都不一样) 一定要保证锁定对象不发生变化 >ThreadLocal辨析 * 为每个线程都有变量的副本,线程之间的隔离,Spring在使用事务的时候就是用的ThreadLocal(如数据库连接) * Thread不保证变量的同步,只保证变量的隔离 threadLocal.get() threadLocal.set() ![0ba1c6aa8c4cafc42640e8aafa5a10d9.png](en-resource://database/3286:1) >ThreadLocal引发内存泄漏分析 * 强引用 如:Object ob=new Object();,该实例不会被回收 * 软引用 SoReference GC回收足够了就不会回收 * 弱引用 只要发生了GC,就一定会回收 * 虚引用 发生GC的时候,通知一下 ![35bc25d8b08b9ac16504236a4eb4695f.png](en-resource://database/3288:1) 想要避免内存泄漏,一定要在用户之后执行ThreadLocal.remove()方法 ThreadLocal.set();会一定程度的回收 >ThreadLocal的线程不安全 static变量会导致线程共享 >线程间的协作 1. 等待和通知 1. wait() 1. 其会释放线程所持有的锁 2. notify/notifyAll 1. 其不会释放锁 2. 一般放在syn关键字的最后一行 3. 尽量用notifyAll,notify只会唤醒一个,不知道唤醒的是否是想唤醒的 3. 等待和通知的标准范式 1. 上述两种必须包裹在syn关键字中 4. notify和notifyAll应该用谁? 5. 等待超时模式实现一个连接池 调用yield(),sleep(),wait(),notify() 方法的影响? yield():让出CPU执行权,不会让出锁 sleep():不会让出锁,要等到休眠 wait():释放自己的锁,唤醒后重新竞争锁 notify():不会释放锁,一般放在同步代码块的最后一行 内存不够用叫内存溢出 回收不掉叫内存泄漏 * * * ### 2. 线程的并发工具类 ### Fork-join 分而治之:分割相同的小问题,小问题之间无关联 动态规划:分割相同的小问题,但小问题之间有关联 十大计算机经典算法:快速排序,堆排序,归并排序,二分查找、线性查找、深度优先、广度优先、Dijkstra、动态规划、朴素贝叶斯分类 分而治之:归并,二分,快排 归并排序:对于给定的一组数据,利用递归和分治计数将数据序列划分为越来越小的半子表,再对半子表排序后,再用递归方法将排好序的半子表合并程越来越大的有序对列 为了提升性能,有时再半子表的个数小于某个数的情况下,对半子表的排序采用其他的排序算法,比如插入排序 若将两个有序表合并程一个有序表,称为2-路归并,与之对应的还有多路归并 ![35edd7bdd8170ff101d8a5fceed3ed54.png](en-resource://database/3290:1) >工作密取:假如某个线程任务做完了,偷偷到其他线程偷个任务来做,做完又放回去(总是从尾部去偷) 使用范式 1. pool=new ForkJoinPool(); 2. Mytask mytask=new ForkJoinTask(); 3. pool.invoke(myTask); 4. Result=myTask.join(); Fork/Join同步演示 Fork/Join异步演示 数据量越大,越能体现ForkJoin优势 task.join();是阻塞方法 #### CountDownLatch的作用,应用场景和实战(通过AQS实现) CountDownLatch是个发令枪,初始化工作(初始化线程去做,n) 主线程再执行要执行代码 计数器和线程数可以不一样 当定义的闭锁扣除点扣除完毕,主线程和业务线程才会继续 压测可以用到 #### CyclicBarrier的作用,应用场景和实战(可重复使用的屏障) 等到所有线程到屏障后,才会执行后续任务 barrier.await()方法可以反复使用,CountDownLatch不能重复使用,扣减之后就无了 跟线程数密切相关,CountDownLatch跟线程无关 #### Semaphore的作用,应用场景和实战(类似许可证,只有许可的线程才能执行通过) 主要用于做流控,做信号量管理 1. 用Semaphore实现数据库连接词 2. Semaphore注意事项 1. 其允许new出来的许可证往里放,需要对空位进行管理(重要) #### Exchange的作用,应用场景和实战 用于两个线程之间的数据交换,最多支持两个线程 #### Callable, Future和FutureTask Runnable的run方法没有返回值,Callable有返回值 FutureTask可以当作一个Runnable来使用,也可以通过Future接口把Callable交给它执行 ### 3. 原子操作CAS 它就是乐观锁的一个体现,JDK大量使用了CAS机制 什么是原子操作? 非常像事务,要么全部成功,要么全部不成功 加锁属于典型的原子操作,加锁是典型的悲观锁(总有刁民想害朕) 悲观锁锁不好会导致死锁 cas原理:利用了现代处理器都支持的CAS的指令,循环这个指令,直到成功为止 ##### cas的问题 1. ABA问题 1. 取值时加版本号可以规避ABA 2. 开销问题 1. 自旋死循环会带来很大的开销,自旋就是不断试,到成功为止! 3. 智能保证一个共享的原子操作 原子操作类的使用 1. 更新基本类型 1. AtomicBoolean 2. AtomicInteger 3. AtomicLong 2. 更新数组类 1. AtomicIntegerArray,.... 3. 更新引用类型 1. AtomicReference, 2. AtomicMarkableReference(可以解决ABA问题) 2. AtomicStampedReference(可以解决ABA,带版本戳) 4. 原子更新字段类 1. AtomicReferenceFilIdUpdater 2. .. ![960c2da463a1382519b2811dc18a53f5.png](en-resource://database/3292:1) 加锁很重量性能低,CAS原子就可以提供性能 ### 4. 显示锁和AQS #### 显示锁(基于AQS实现) * Lock接口和核心方法 * Lock * unLock(释放锁,一定要放在finaly语句块中) * tryLock * synchronized关键字和Lock比较 * 优先使用syn,syn优化好,消耗也比显示锁小 * Lock拿锁过程中允许中断 * Lock允许设置拿锁超时时间 * 实现读写锁可以考虑Lock * 可重入锁ReentrantLock()(默认非公平) * 当线程递归调用时,一个线程获得这把锁,下次再调用可以拿到同一把锁,syn已经实线了可重入 * 锁的公平和非公平 * syn加锁,如果3个线程抢锁,排队先到先得,即为公平锁 * 非公平锁:可能最新线程拿到锁,效率更高,公平锁需要上下文切换唤醒 * ReadWriteLock接口和读写锁ReentrandReadWriteLock (读多写少,读写分离) * 写独占,读共享,读写互斥 * 可以让读操作,并行进行 * Condition接口 * 实现了等待通知 * 类似于syn中的wait(),notifly * 用Lock和Condition实现等待通知 * #### 了解LockSupport工具 * 作用 * 阻塞一个线程(park) * 唤醒一个线程 (unpark) * 构建同步组件的基础工具 * park开头的方法 * unpark(Thread thread)方法 #### CLH队列锁(基本思想) > 基于链表的一种视线 > 有多个节点,结点有一个锁和一个域,域不断的检测前面的队列中域的锁标识是否释放,通过则获取锁 #### AQS深入分析 >是CLH队列锁的一种变体实现,是实现公平锁的一种方式,是一个同步组件,不是锁 > Node结构,头尾结构,双向链表 > 所有jdk提供的锁,都是可重入锁 * AQS使用方式和其中的设计模式 * 使用了模板方法,(定义一个操作算法的骨架子,把某些步骤放到子类去实现,template) * 了解其中的方法 * 实现一个类似于ReentrantLock的锁 AQS中的数据结构-节点和同步队列 ![6ab885bcd80c8917a74957bf0e961b48.png](en-resource://database/3294:1) 单web应用,数据库行锁,使用内存锁 多web应用推荐使用reids和zk 多线程去抢本地锁-->抢分布式锁,可以提供性能 #### 节点再同步队列中的增加和移出 ![341b6215e63028e425225e8cad0c40ad.png](en-resource://database/3296:1) #### 独占式同步状态获取与释放 同步器AQS内部会维护一个队列,当多个线程获取一个锁,获取失败的线程通过CAS加入队列,并成为尾结点,加入后,会在队列上自旋 当有锁被释放,他会唤醒后续结点,会尝试去拿锁 ![3380ce2c8f4b9123012e8bde067519e5.png](en-resource://database/3298:1) #### 其他同步状态获取与释放 * 共享式同步状态获取与释放 * 独占式超时同步状态获取 * 每被唤醒一次就会重新计算超时时间 * 再次实战,实现一个奇葩的三元共享同步工具类 #### Condition分析 * 一个Condition包含一个等待队列 * 同步队列与等待队列 #### 节点在队列之间的移动 1. await() ![216d76d3271dbbc7956a3827fc220419.png](en-resource://database/3300:1) 2. signal() 1. signalAll():condition多个实线,用signal就ok,siganl唤醒的时,signalAll()唤醒所有节点,消耗性能 2. 没有特殊情况用signal()来唤醒线程 ![295b8108c312780ba01edfeba95ff123.png](en-resource://database/3302:1) #### 回头看Lock的实现 * 了解ReentrantLock的视线 * 锁的可重入 * 公平和非公平 * 非:先抢一次再说 * 公:排队 * 将类似于ReentrantLock的锁的视线修正为可重入 * 了解ReentrantReadWritelLock的实现 ### 5. 并发容器 #### Hash >把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列(压缩算法 **解决哈希冲突** 1. 开放寻址 2. 再散列 3. 链地址法 #### 位运算 java中常见用运算 * 位与& * 位或| * 位与~ * 位异或^ * 有符号右移>>(若正数,高位补0,负数,高位补1) * 有符号左移<< * 无符号右移>>>(不论正负,高位均补0) 位运算使用场景 1. java类中的类修饰符,成员变了修饰符,方法修饰符 2. java容器中的HashMap和ConcurrentHashMap的实线 3. 权限空值或商品属性 4. 简单可逆加密(1^1=0,0^1=1) 在map里的数组个数一定是2的乘方数,计算key值在哪个元素中的时候,就用位运算来快速定位 优点: 1. 节省很多代码量 2. 效率很高 3. 属性变动影响小 缺点: 1. 不直观 #### jdk1.7 HashMap死循环分析 头插法 1. 取当前的table的2倍作为新table的大小 2. 根据算出的新table的大小new出一个新的Entry数组来,名为newTable 3. 轮询原table的每一个位址,将每个位址上连接Entry,算出再新table上的位址,并以链表形式连接 4. 原table上面的所有entry以及已到了新的table上,HashMap中的table指向newTable >jdk1.7 HashMap死循环简述:当两个线程并发执行扩容操作,一个完成,一个挂起,恢复执行以后,内部需要头插法链表操作,next指针会相互指针,所以会造成死循环 #### ConcurrentHashMap 除了提供线程安全的get,put等方法,ConcurrentHashMap还提供了一个在并发下比较有用的方法 putlfAbsent * 1.7jdk * 数组+链表 ![f13709a229f7f78fbdd8fee71c0d16e1.png](en-resource://database/3304:1) *Segment初始化后无法扩容 分表2的倍数 扩容:Segment下的table数组不够了,把结点重新进行哈希计算得到一个新数组,然后迁移到新数组中,把新数组取代老数组 * 1.8jdk 数组+链表+红黑树 链表转换为树 使用步长并发扩容 #### 并发下Map常见面试题汇总 HashMap和HashTable有什么区别? 1. HashMap线程不安全,HashTable线程安全,效率低一些 2. HashMap允许key为value,HashTable不可以 3. HashMap默认初始化为16,HashTable为11 4. hmap默认缺省扩大两部,htbale默认扩大两部加一 5. hmap再散列的时候会重新计算hash,htbale直接使用对象的hashcode值 java中另一个线程安全的于HashMap极其类似的类是什么?同样是线程安全,它与HashTable在线程同步上有什么不同? 1. ConcurrentHashMap,htable方法上用了很多syn关键字,效率更低 2. cmap使用了分段锁,1.8使用了CAS+syn+分段锁,效率更高 HashMap&ConcurrentHashMap的区别? 1. 除了线程安全,基本上没有太大差别 2. hmap键值对允许有null,cmap不允许 3. 数据结构上不同 为什么ConcurrentHashMap比HashTable效率要搞? cmap用了分段锁,htalbe用了一把锁 ConcurrentHashMap锁机制具体分析(JDK1.7 VS JDK1.8)? 1.分段锁,底层采用数组+链表结构 2.分段锁,底层采用数组+红黑树+链表,红黑树和链表相互转换,提升性能 ConcurrentHashMap在jdk1.8中,为什么要使用内置锁synchronized来代替重入锁ReentrantLock? 提升性能 1.8下ConcurrentHashMap简单介绍? 略 ConcurrentHashMap的并发度是什么? 将初始容量提升至并发度大小 #### 更多的并发容器 ConcurrentSkipListMap和ConcurrentSkipListSet ,有序队列 了解什么是SkipList? SkipList跳表,这是数据结构的一种,好处:提高查询速度,跳表思想类似于索引 二分,二叉查找树--->链表 平衡, AVL 红黑树 ConcurrentLinkedQueue(无界非阻塞队列) LinkedList的并发版本 写时复制容器 * CopyOnWriteArrayList,拷贝副本到容器,读旧容器,写新容器。内存开销大 * CopyOnWriteArraySet #### 阻塞队列 * 概念,生产者消费模式 BlockingQueue * 队列:先进先出 * 阻塞的插入,队列满 * 阻塞的移出,队列空 * 常用方法 * 常用阻塞队列 * ArrayBlockingQueue:数组结构组成的有界阻塞队列,锁没分离 * LinkedBlockingQueue:链表结构,锁分离,占用内存比ABQ多 * PriorityBlockingQueue:支持优先级的无界 * DelayQueue:使用优先级队列视线的无界 * SynchronousQueue:不存储源氏的阻塞队列 * LinkedTansferQueue:链表结构组成的无界 * LinkedBlockingDeue:链表结构组成的双向阻塞,工作密取用到了双向阻塞 * 阻塞队列的实现原理 * 等待通知模式 * 有界和无界(尽量不要使用无界) * 有界:长度有限,满了会阻塞 * 无界:长度有限,但不会满 其他:TreeSet是TreeMap的包装 ### 6. 线程池和Exector框架 #### 线程池 好处: 1. 降低资源消耗 2. 提高响应速度 3. 提高线程的可管理性 JDK的线程池和工作机制 * 线程池的创建() * 各个参数的含义 * corePoolSize * maximumPoolSize 最大线程数 * keepAliverTime 线程空闲存活时间 * TimeUnit unit * ThreadFactory 线程池工厂 * BlockingQueue * ..... * 提交任务 * 关闭线程池 * 合理配置线程池 * 任务特性 * cpu密集型(取值不要超过CPU核心数+1) * IO密集型(磁盘,网络)机器CPU核心*2 * 混合型(CPU密集,IO密集)尽量拆分, Cup很忙(8核)10线程 实际开发,尽量用有界队列 #### Executor框架 ![bae6cd795530c8a6d23faa5fe7690419.png](en-resource://database/3306:1) 骨架 接口-->抽象类-->具体实现类 ExecutorService threadPool=new ThreadPoolExecutro(....); threadPool.shutdown();中断所有没有(空闲)执行完的线程 threadPool.shutdownNow(); 中断不一定要执行完 了解CompletionService接口 谁先执行完,谁先拿到结果,线程和阻塞队列的合体 (比喻:抢红包) 自己执行,任务拿结果,要按提交的拿 #### 线程池 系统为我们预定义的线程池详解 FixedThreadPool SingleThreadExecutro CachedThreadPool WorkStealingPool ScheduledThreadPoolExecutor(定期执行任务) #### 自定义线程池 ### 7. 线程安全 #### 线程安全 什么是线程的安全性?怎么才能做到线程安全? >当有多个线程访问某个类的时间,不管运行环境采用何种方式调度,或线程交替执行,调用代码中不需要任何额外的同步和协同,都能表示出正确的行为 * 栈封闭 (如方法中的局部变量,只有方法能看的,线程封闭) * ad-hoc * 栈封闭 * ThreadLocal * 无状态的类 (类中没有任何的成员变量,即为无状态类,是线程安全的) * 让类不可变 (变量加final关键字) * volatile * 加锁和CAS * 并不是所有加锁都能保证线程安全,比如死锁 * 安全的 发布 (如使用Collections.sysnchronizedList()进行包装) * 如果时用户自定义对象,第一可以继承 * 委托给线程安全类来做 * ThreadLocal (带副本,线程安全) 其他:Serlvet不是线程安全的 HashMap用空间换时间 #### 线程不安全引发的问题 * 死锁 * 有哪些死锁? * 简单的 * 动态的 * 怎么解决死锁? * 其他死锁问题 * 活锁 * A(a)-->A(a) * B (b)-->B (b) * 线程饥饿 * 低优先级锁总是拿不到CPU执行时间 * 性能和思考 1. 影响性能的因素 1. 上下文切换 2. 内存同步 3. 。。。 2. 减少锁的竞争 1. 缩小锁的范围 2. 减少锁的粒度 3. 锁分段 4. 替换独占锁 5. 避免多余的锁 死锁代码: 两个锁, 两个线程,拿的顺序想法 先保证程序正确,确实达不到性能要求,再去优化(黄金原则) #### 线程安全的单例模式 用双重检查的单例模式,真实是线程安全的吗? * 解决之道 * 懒汉式 * 饿汉式 ### 8. 实战项目-并发任务执行框架 框架业务示意图 ![b5bd2fa3698a84b98f75a5cffa03b242.png](en-resource://database/3308:1) 框架流程图 ![4d5396ecbd5ee01df926046da42fea7b.png](en-resource://database/3310:1) 1. 提高性能,采用多线程,屏蔽细节 1. 封装线程池和阻塞队列 2. 每个批量任务拥有子集的上下文黄金 1.需要一个并发安全的容器保存每个任务 3. 自动清除已完成和过期的任务 1. 定时轮询 ### 9. 实战项目-性能优化实战 #### 上面项目存在的问题 * 一份PDF文档生成平均时长在50-55秒左右 * 为什么慢? * 分离出需要处理的题目 * 解析处理题目文本,对题目中的图片下载到本地,然后调用第三方工具生成PDF文档(耗时大约40-45秒) * 将PDF文档上传到云空间进行存储(耗时9-10秒) * 提供文档地址让用户去下载打印 #### 分析和改进 架构改进之路 服务化,文档生成并行化,采用生产者消费者模式 ![cf298ae0eb57aa88bd5c295545d37caa.png](en-resource://database/3312:1) 考察业务特点 1. 从容量为10万左右的题库中为每个学生抽取适合他的题目 2. 每到题目都含有大量的图片需要下载到本地,和文字部分一起渲染 3. 存在热点题目 1. 解决方案:缓存避免重复工作,题目处理并行和异步化 2. 如何实现 1. 先检索缓存,新题目在生成时要考虑并发安全和充分利用Future 2. 题目有更新时,怎么处理? #### 改进之后的处理流程 ![b2fe751ad552a7345858922900e107e2.png](en-resource://database/3314:1) #### 能否继续改进? LRU策略(最近最少使用原则) 1. 题库数量太多时,如何处理? 2. 服务器重启的时候,已缓存的题目怎么办? 3. 还可以继续优化吗? 启示: 1. 性能优化不能是空中楼阁,业务深入分析 2. 善用语言的高并发特性 3. 多利用缓存和异步化处理机制 多级缓存 ### 10. JMM和底层原理 #### JMM基础-计算机原理)(内存模型) Jeff Dean 谷歌首席架构师 ![d64a595e84068d82c41ee470000d4440.png](en-resource://database/3316:1) 把运算要用的数据赋值到缓存里面,让运算可以快速进行,运算结束,吧缓存同步到内存之中 #### 物理内存模型带来的问题 ![cf739fc97f99e3daf522511bb8d10a77.png](en-resource://database/3318:1) #### 你所不知道的伪共享 ![20902627c2441aa901a4787dad2ff3bd.png](en-resource://database/3320:1) 两个线程访问同一个缓存中的两个东西 怎么解决伪共享? 1. 数据填充 2. 注解 #### java内存模型和导致的问题 ![4ceb77affc1174b23cb7faa5f6da59c1.png](en-resource://database/3324:1) #### 如何解决在并发下的问题 ![c7ef7088aa43b966a4ff49e3663a99cb.png](en-resource://database/3322:1) 内存屏障 ![52f34524bc8acf082c0e6c8a9f6f6fab.png](en-resource://database/3326:1) 1. 保持某些特定操作的执行顺序,禁止某些重排序 2. 影响某些结果的内存可见性,强制刷出cash #### 临界区 ![997c5271a4807506e6bee731e57b31ee.png](en-resource://database/3328:1) #### Happens-Before ![e3b973bca37e59d439c2869dece5c8c1.png](en-resource://database/3330:1) 定义 1. 在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么者两个操作之间必须要存在happens-before关系 2. 两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行,happens-before仅仅要求前一个操作对后一个操作可见,且前一个操作按顺序排在第二个操作之前 加深理解 1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。 2. 两个操作之间存在happens-before关系,并不意味着java平台的具体视线必须要按照happens-before 关系指定的顺序来执行。如果重排序之后的执行结构,与按happens-befopre关系来执行的结果一致,那么这种重排序十允许的(对编译处理器来说) #### Happens-Before规则 无需任何同步手段就可以保证的 1. 程序顺序规则 2. 监视器锁规则 3. volatiole变量规则 4. 传递性 5. start()规则 6. join规则 7. 线程中断规则 #### volatile 1. 可见性 2. 原子性(仅仅单个能保证原子性,有复合操作无法保证) ![fabe12a283cb47a8930379ddae9f541a.png](en-resource://database/3332:1) #### volatile内存语义 #### 为何volatile不是线程安全的 ![5f333f2ce27243d69d707e5217246c85.png](en-resource://database/3334:0) #### final的内存语义 编译器和处理器都要遵守两个重排序规则 编码原则:平时写代码,只要不需要set去修改,一定要加final关键字 #### 锁的内存语义 ### 11. JAVA8新增并发 #### 新增原子操作 LongAdder:更快的原子类 (空间换时间) 设计思想:分离热点 #### 新增显示锁 StampLock:读写锁的改进 其提供了一种乐观的的读策略,这种乐观策略的锁非常类似于无锁的操作,使得乐观锁完全不会阻塞线程 #### CompleteableFuture 改进 Future的不足 1. 结果的获取不方便 2. 很难直接表述多个Future结果之间的依赖性 CompleteableFuture 1. 实现了Future ,Comp;etionStage两个接口,可以实现异步调用 2. 基本用法 #### lambad表达式 1. 可以看成是匿名内部类的一个简洁写法 2. 语法分为三部分,参数列表,箭头,主体, 3. 主要用于函数式接口上 ![311dfe7cadbde69c81a9052f47c1f366.png](en-resource://database/3338:0) #### Disruptor 是一个高性能的线程简异步通信的框架 传统队列的问题 加锁就有性能问题 伪共享问题 高性能的原理 #### 总复习和常见并发面试题 * 在java中守护线程和用户线程的区别? * 线程与进程的区别? * 什么是多线程中的上下文切换? * 死锁与活锁的区别,死锁与饥饿的区别? * synchronized底层视线原理? * 什么是线程组,为什么在Java中不推荐使用? * 什么是Executors框架?为什么使用Executor宽街? * 在java中Executor和Executors的区别? * 什么是原子操作?在java Concurrrency API中有哪些原子类? * java Concurrency API 中的Lock接口是什么?对比synchronized它有什么优势? * 什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实线生产者-消费者模型? * 什么是Callable和Future? * 什么是FutureTask? * 什么事并发容器的实现? * 多线程同步和互斥有几种实线方法,都是什么? * 什么是竞争条件? * 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法? * 在java中CycliBarriar 和CountdownLatch有什么区别? * 什么时不可表对象,它对写并发应用有什么帮助? * notify()和notifyAll()有什么区别? * 什么时可重入锁(ReentrantLock)?谈谈它的实现? * 当一个线程进入某个对象的一个synchronized的实例方法后,其他线程是否可进入此对象的其他方法? * 乐观锁和悲观锁的理解以及如何视线?有哪些实线方式? * 什么时CAS操作,缺点是什么? * synchronizedMap和ConcurentHashMap有什么区别? * 写时复制容器可以用于什么应用场景? * volatile有什么用?能否用一句话说明下volatile的应用场景? * 为什么代码会重排序? * 在java中wait和sleep方法的不同? * 一个线程运行时发生异常会怎样? * 为什么wait,notify和notifyAll这些方法不在thread类里面? * 什么是ThreadLocal变量? * Java中interrupted和isInterrupted方法的区别? * 为什么wait和notify方法要在同步块中调用 * 为什么你应该在循环中检查等待条件? * 怎么检测一个线程是否拥有锁? * 你如何在java中获取线程堆栈? * Java线程池中submit和execute方法有什么区别? * 你对线程优先级的理解是什么? * 你如何确保main()方法所在的线程是Java程序最后结束的线程? * 为什么Thread类的sleep()和yield()方法是静态的 * 现在有T1,T2,T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行后执行? * 你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保证它的完整性,你会怎样去实线它? * 用java实现阻塞队列 * 用java写代码来解决生产者-消费者问题 * 用java写一个会导致死锁的程序,你将怎么解决? * java中如何停止一个线程? * jvm中哪个参数是用来控制线程的堆栈大小的 * 如果同步块内的线程抛出异常发生什么? * 单例模式的双重检测实现是什么?为什么不安全?如何在java中创建线程安全的singleton? * 写出3调你遵循的多线程最佳实践 * 请概述现称此的创建参数,怎样么合理配置一个线程池的参数? * 请概述锁定公平和非公平,jdk内部是如何实现的。 * 请概述AQS * 它是构建锁和其他同步锁的框架 * 模板方法设计模式 * 请概述Volatile * 保证了多线程的可见性 * 但是不能保证原子性