个人珍藏的80道多线程并发面试题(1-10答案解析)
2021-04-06 19:26
个人珍藏的80道Java多线程/并发经典面试题,因为篇幅太长,现在先给出1-10的答案解析哈,后面一起完善,并且上传github哈~ https://github.com/whx123/JavaHome 「公众号:捡田螺的小男孩」 ObjectMonitor的几个关键属性 _count、_recursions、_owner、_WaitSet、 _EntryList 体现了monitor的工作原理
在讨论锁优化前,先看看JAVA对象头(32位JVM)中Mark Word的结构图吧~ Mark Word存储对象自身的运行数据,如「哈希码、GC分代年龄、锁状态标志、偏向时间戳(Epoch)」 等,为什么区分「偏向锁、轻量级锁、重量级锁」等几种锁状态呢? 在JDK1.6之前,synchronized的实现直接调用ObjectMonitor的enter和exit,这种锁被称之为「重量级锁」。从JDK6开始,HotSpot虚拟机开发团队对Java中的锁进行优化,如增加了适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等优化策略。 举个例子,买门票进动物园。老师带一群小朋友去参观,验票员如果知道他们是个集体,就可以把他们看成一个整体(锁租化),一次性验票过,而不需要一个个找他们验票。 有兴趣的朋友们可以看看我这篇文章:
Synchronized解析——如果你愿意一层一层剥开我的心[1] 回答四个主要点: ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。 ThreadLocal内存结构图:
由结构图是可以看出: 对照着几段关键源码来看,更容易理解一点哈~ ThreadLocal中的关键方法set()和get() ThreadLocalMap的Entry数组 所以怎么回答「ThreadLocal的实现原理」?如下,最好是能结合以上结构图一起说明哈~ 先看看一下的TreadLocal的引用示意图哈, ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用,如下
弱引用:只要垃圾回收机制一运行,不管JVM的内存空间是否充足,都会回收该对象占用的内存。 弱引用比较容易被回收。因此,如果ThreadLocal(ThreadLocalMap的Key)被垃圾回收器回收了,但是因为ThreadLocalMap生命周期和Thread是一样的,它这时候如果不被回收,就会出现这种情况:ThreadLocalMap的key没了,value还在,这就会「造成了内存泄漏问题」。 如何「解决内存泄漏问题」?使用完ThreadLocal后,及时调用remove()方法释放内存空间。 我记得校招的时候,这道面试题出现的频率还是挺高的~可以从锁的实现、功能特点、性能等几个维度去回答这个问题, 举个例子吧: Fork/Join框架是Java7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。 Fork/Join框架需要理解两个点,「分而治之」和「工作窃取算法」。 「分而治之」 以上Fork/Join框架的定义,就是分而治之思想的体现啦
「工作窃取算法」 把大任务拆分成小任务,放到不同队列执行,交由不同的线程分别执行时。有的线程优先把自己负责的任务执行完了,其他线程还在慢慢悠悠处理自己的任务,这时候为了充分提高效率,就需要工作盗窃算法啦~ 工作盗窃算法就是,「某个线程从其他队列中窃取任务进行执行的过程」。一般就是指做得快的线程(盗窃线程)抢慢的线程的任务来做,同时为了减少锁竞争,通常使用双端队列,即快线程和慢线程各在一端。 看看Thread的start方法说明哈~
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
*
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
......
}
JVM执行start方法,会另起一条线程执行thread的run方法,这才起到多线程的效果~ 「为什么我们不能直接调用run()方法?」
如果直接调用Thread的run()方法,其方法还是运行在主线程中,没有起到多线程效果。 CAS,Compare and Swap,比较并交换; CAS 涉及3个操作数,内存地址值V,预期原值A,新值B;
如果内存位置的值V与预期原A值相匹配,就更新为新值B,否则不更新 CAS有什么缺陷? 「ABA 问题」 并发环境下,假设初始条件是A,去修改数据时,发现是A就会执行修改。但是看到的虽然是A,中间可能发生了A变B,B又变回A的情况。此时A已经非彼A,数据即使成功修改,也可能有问题。 可以通过AtomicStampedReference「解决ABA问题」,它,一个带有标记的原子引用类,通过控制变量值的版本来保证CAS的正确性。 「循环时间长开销」 自旋CAS,如果一直循环执行,一直不成功,会给CPU带来非常大的执行开销。 很多时候,CAS思想体现,是有个自旋次数的,就是为了避开这个耗时问题~ 「只能保证一个变量的原子操作。」 CAS 保证的是对一个变量执行操作的原子性,如果对多个变量操作时,CAS 目前无法直接保证操作的原子性的。 可以通过这两个方式解决这个问题: 有兴趣的朋友可以看看我之前的这篇实战文章哈~
CAS乐观锁解决并发问题的一次实践[2] 没有代码demo,感觉是没有灵魂的~ 如下: 运行结果: 死锁是指多个线程因竞争资源而造成的一种互相等待的僵局。如图感受一下:
「死锁的四个必要条件:」 「如何预防死锁?」 牛顿说,我之所以看得远,是因为我站在巨人的肩膀上~ 谢谢以下各位前辈哈~ Synchronized解析——如果你愿意一层一层剥开我的心: https://juejin.im/post/5d5374076fb9a06ac76da894#comment CAS乐观锁解决并发问题的一次实践: https://juejin.im/post/5d0616ade51d457756536791 面试必问的CAS,你懂了吗?: https://blog.csdn.net/v123411739/article/details/79561458 Java多线程:死锁: https://www.cnblogs.com/xiaoxi/p/8311034.html ReenTrantLock可重入锁(和synchronized的区别)总结: https://blog.csdn.net/qq838642798/article/details/65441415 聊聊并发(八)——Fork/Join 框架介绍: https://www.infoq.cn/article/fork-join-introduction 个人珍藏的80道多线程并发面试题(1-10答案解析) 标签:tail spin down rom 意图 max running 同步方法 中断 原文地址:https://www.cnblogs.com/jay-huaxiao/p/13394928.html
前言
?
1. synchronized的实现原理以及锁优化?
synchronized的实现原理
ObjectMonitor() {
_header = NULL;
_count = 0; // 记录线程获取锁的次数
_waiters = 0,
_recursions = 0; //锁的重入次数
_object = NULL;
_owner = NULL; // 指向持有ObjectMonitor对象的线程
_WaitSet = NULL; // 处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // 处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
锁优化
?
?
2. ThreadLocal原理,使用注意点,应用场景有哪些?
ThreadLocal是什么?
//创建一个ThreadLocal变量
static ThreadLocal
ThreadLocal原理
public class Thread implements Runnable {
//ThreadLocal.ThreadLocalMap是Thread的属性
ThreadLocal.ThreadLocalMap threadLocals = null;
}
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程t
ThreadLocalMap map = getMap(t); //根据当前线程获取到ThreadLocalMap
if (map != null)
map.set(this, value); //K,V设置到ThreadLocalMap中
else
createMap(t, value); //创建一个新的ThreadLocalMap
}
public T get() {
Thread t = Thread.currentThread();//获取当前线程t
ThreadLocalMap map = getMap(t);//根据当前线程获取到ThreadLocalMap
if (map != null) {
//由this(即ThreadLoca对象)得到对应的Value,即ThreadLocal的泛型值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
static class ThreadLocalMap {
static class Entry extends WeakReference
?
?
ThreadLocal 内存泄露问题
?
ThreadLocal的应用场景
3. synchronized和ReentrantLock的区别?
?
?
4. 说说CountDownLatch与CyclicBarrier区别
?
?
5. Fork/Join框架的理解
?
6. 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the
run
method of this thread.
* start
method) and the other thread (which executes its
* run
method).
*
7. CAS?CAS 有什么缺陷,如何解决?
?
?
?
?
?
?
9. 如何保证多线程下i++ 结果正确?
/**
* @Author 捡田螺的小男孩
*/
public class AtomicIntegerTest {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
testIAdd();
}
private static void testIAdd() throws InterruptedException {
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i executorService.execute(() -> {
for (int j = 0; j //自增并返回当前值
int andIncrement = atomicInteger.incrementAndGet();
System.out.println("线程:" + Thread.currentThread().getName() + " count=" + andIncrement);
}
});
}
executorService.shutdown();
Thread.sleep(100);
System.out.println("最终结果是 :" + atomicInteger.get());
}
}
...
线程:pool-1-thread-1 count=1997
线程:pool-1-thread-1 count=1998
线程:pool-1-thread-1 count=1999
线程:pool-1-thread-2 count=315
线程:pool-1-thread-2 count=2000
最终结果是 :2000
10. 如何检测死锁?怎么预防死锁?死锁四个必要条件
参考与感谢
个人公众号
Reference
文章标题:个人珍藏的80道多线程并发面试题(1-10答案解析)
文章链接:http://soscw.com/index.php/essay/72127.html