费曼学习法的精髓在于用自己的语言把学习的知识给他人讲懂
下面有一些问题,并附有答案和对该知识点的详解,如果你能用自己的语言把该问题讲解清楚,那恭喜你,该知识点你掌握了!
1. JUC包中常见的类都有哪些?
JUC包中有很多并发编程需要的工具类,可按照以下几种类别进行梳理:
- ThreadLocal,ThreadLocalRandom
- 原子操作类:AtomicInteger等
- 锁:ReentrantLock,ReentrantReadWriteLock
- 同步器:Semaphore,CoundDownLatch,CyclicBarrier
- 同步Map:ConcurrentHashMap
- 同步List:CopyOnWriteArrayList
- 同步队列:ConcurrentLinkedQueue,LinkedBlockingQueue,ArrayBlockingQueue
详细原理戳这里: JUC包中常用工具类的简单介绍和使用说明(上)
2. Java的synchronized关键字怎么用?其实现原理是什么?
synchronized关键字可以使线程对临界区互斥访问,synchronized可以修饰类静态方法、类普通方法和普通代码块。当线程执行到synchronized修饰的代码时,其首先要去获取一个内部锁,只有拿到锁之后才能访问临界区,拿不到锁的线程就会挂起等待锁的释放。
synchronized能够进行线程同步的基础是:Java中的每一个对象都可以当作一个锁。每一个对象都有一个与之关联的Monitor,在对象头的Mark Word中记录了这个Monitor的地址,JVM内部对线程的挂起和唤醒是通过这个Monitor实现的。
详细原理戳这里: 详解Java中synchronized的实现原理
3. Java的synchronized关键字和Lock有什么区别?
synchronized是java的关键字,是jvm层面的;Lock是一个接口,具体实现类有ReentrantLock。如果有可中断、公平锁、控制锁时长等需求,必须用Lock,但synchronized语法更简洁。使用Lock要记得在finally中释放锁,不然容易造成线程死锁;而采用synchronized不需要用户去手动释放锁
详细原理戳这里: 详解Java中synchronized的实现原理
4. Java并发中的CAS操作是什么?
CAS,Compare And Swap,是乐观锁解决冲突的一种方式。CAS操作有三个参数,内存地址V,期待值A,修改值B,只有当V的值等于A时,才将V的值修改为B,否则不做操作。Java中的CAS操作是Unsafe类中相应的函数直接被虚拟机生成及其相关的CAS原子指令来实现的。
详细原理戳这里: 当我谈CAS时,我谈些什么
5. Java并发中的CAS操作有什么缺点?
CAS操作有三个缺点:
- 循环耗时问题,如果CAS长期不成功,则很消耗CPU时间
- 只能操作一个原子变量
- ABA问题,当一个线程的期待值是A,其它线程把值改为B又改回A时,该线程无法直到值已被修改。
详细原理戳这里: 当我谈CAS时,我谈些什么
6. Java中的volatile有什么作用?
volatile主要提供内存可见性的保证,也就是说:
- 当对一个volatile变量进行写操作时,JMM会把该线程中的共享变量刷新到主内存中去
- 当对一个volatile变量进行读操作时,JMM会把当前线程的本地内存中的变量置为无效,然后从主内存中读取最新值。
但需要注意的是,volatile不能保证复合操作的原子性,例如a++
等操作,在使用volatile变量来保证线程安全时要特别注意这一点。
详细原理戳这里: Java中的volatile简介
7. Java中的volatile和synchronized有什么区别?
volatile修饰变量,而synchronized修饰方法和代码块。volatile只能保证内存可见性,不能保证原子性,而synchronized都可以保证。
从功能上讲,volatile的功能只是synchronized的一个子集,但volatile不需要阻塞线程,因此性能更好一些。
详细原理戳这里: Java中的volatile简介、 详解Java中synchronized的实现原理
8. Java中的ThreadLocal的实现原理是什么?
当多线程访问ThreadLocal共享变量时,会在线程的本地内存中产生一个副本,以后对该共享变量的操作都是对本地内存中的副本变量操作,因此解决了多线程竞争问题。
ThreadLocal的实现原理:Thread类中有一个threadLocals的成员变量,是一个Map,其key为ThreadLocal的弱引用,其value为set方法传入的值。当我们使用ThreadLocal中的set方法时,首先获取当前线程的threadLocals哈希表,再以当前对象的this引用为key进行set操作。
详细原理戳这里: 详解JUC中ThreadLocal的实现原理
9. Java中的ThreadLocal有什么缺点?
ThreadLocal的缺点有二,第一是使用不当容易造成内存泄漏,第二是处理哈希冲突时使用线性探测法,在极端情况下性能会退化到O(n)。
关于内存泄漏,使用ThreadLocal时,如果没及时调用remove方法,可能会产生内存泄漏。ThreadLocalMap中key为ThreadLocal的弱引用,在没有其它强引用的情况下遇到GC会被清除掉,但ThreadLocalMap的value仍然是强引用,这时就会出现ThreadLocalMap中key为null但value不为null的情况,此时再调用remove方法已无法清除掉value的强引用,而只要线程存在则ThreadLocalMap就一直存在则该value就永远无法被GC,因此就产生了内存泄漏。
详细原理戳这里: 详解JUC中ThreadLocal的实现原理
10. JUC中的ConcurrentHashMap是如何实现的?
总的来说,ConcurrentHashMap的实现使用了减小锁粒度以减少并发冲突的编程思想。
JDK1.7中的ConcurrentHashMap底层使用Segment数组实现,Segment继承自ReentrantLock,每个Segment管理若干个位桶,每个位桶中的Entry使用链表法来解决哈希冲突。因此,只要多个线程操作的不是同一个Segment,便不会引起竞争。
JDK1.8中的ConcurrentHashMap底层使用数组+链表+红黑树实现,利用synchronized和CAS来进行并发控制,看起来就像是一个经过优化且并发安全的HashMap。锁的粒度是每一个位桶,因此只要多线程操作的不是同一个位桶,便不会引起竞争。它首先使用无锁操作CAS插入头结点,如果插入失败,说明已经有别的线程插入头结点了,再次循环进行操作。如果头结点已经存在,则通过synchronized获得头结点锁,进行后续的操作,性能比segment分段锁又再次提升。
详细原理戳这里: 简析JUC中ConcurrentHashMap的实现原理
11. JUC中的AQS的实现原理是什么?
AQS是一个用来构建锁和同步器的框架,它是一个抽象类,使用了模板方法模式。继承自AQS的自定义同步器在实现时只需要实现共享资源的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在底层实现好了。
AQS底层主要基于一个volatile的state变量和双向队列实现。state变量一般代表共享资源,其在不同的应用场景下具有不同的含义,AQS通过CAS来操作state变量,获取成功则设置当前线程为工作线程,获取失败则把当前线程封装成一个节点插入双向队列并挂起等待,当共享资源被释放时,则再从队列里取一个线程进行唤醒。其中,入队出队操作也是通过CAS来操作队列头尾节点实现的,线程的挂起和唤醒是通过LockSupport工具类来实现的。
详细原理戳这里: 详解JUC中的基础组件AQS
12. JUC中的LockSupport是做什么用的?
LockSupport用来挂起和唤醒线程,其底层基于Unsafe类的native方法实现,其核心方法有park和unpark:
- park:park方法用来挂起线程。若调用park的线程持有一个许可,则park立即返回;若不持有,则该线程挂起。
- unpark:unpark方法用来唤醒线程。若调用unpark的线程没有持有许可,则令其持有;若该线程被挂起,则将其唤醒。
详细原理戳这里: 简析JUC中的LockSupport
13. JUC中的LockSupport和wait/notify方法有什么区别?
- wait必须在synchronized代码块中执行,park可以在任何地方执行
- wait必须捕获中断异常,而park不需要
- wait不带有超时功能,而parkNanos等方法可以有
- notify不能指定唤醒的线程,而park可以
- 如果在wait之前执行了notify,会抛出IllegalMonitorStateException异常;但在park之前执行了unpark,不会有影响,park会直接返回
详细原理戳这里: 简析JUC中的LockSupport
14. Java中ThreadLocal的应用场景是什么?
当一个共享变量的作用并不是用来在线程间通信,而是线程都要使用这个共享变量来完成自己内部的工作时,就可以使用ThreadLocal,从而能避免并发竞争。例如,当多个线程使用同一个Random时,会出现对随机数种子的竞争从而造成性能下降,故产生了ThreadLocalRandom这个类来解决这个问题。
详细原理戳这里: 详解JUC中ThreadLocal的实现原理
【长期更新中......】