自适应平台网站,wordpress弹幕视频主题,塘厦镇属于东莞哪个区,广告公司手机网站模板前言并发编程是面试中必问的知识点之一#xff0c;所以本文整理了一些最为常见的并发面试题#xff0c;一起来看吧~1. synchronized的实现原理以及锁优化#xff1f;synchronized的实现原理synchronized作用于「方法」或者「代码块」#xff0c;保证被修饰的代码在同一时间… 前言并发编程是面试中必问的知识点之一所以本文整理了一些最为常见的并发面试题一起来看吧~1. synchronized的实现原理以及锁优化synchronized的实现原理synchronized作用于「方法」或者「代码块」保证被修饰的代码在同一时间只能被一个线程访问。synchronized修饰代码块时JVM采用「monitorenter、monitorexit」两个指令来实现同步synchronized修饰同步方法时JVM采用「ACC_SYNCHRONIZED」标记符来实现同步monitorenter、monitorexit或者ACC_SYNCHRONIZED都是「基于Monitor实现」的实例对象里有对象头对象头里面有Mark WordMark Word指针指向了「monitor」Monitor其实是一种「同步工具」也可以说是一种「同步机制」。在Java虚拟机HotSpot中Monitor是由「ObjectMonitor实现」的。ObjectMonitor体现出Monitor的工作原理~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 ;}
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中的锁进行优化如增加了适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等优化策略。❞偏向锁在无竞争的情况下把整个同步都消除掉CAS操作都不做。轻量级锁在没有多线程竞争时相对重量级锁减少操作系统互斥量带来的性能消耗。但是如果存在锁竞争除了互斥量本身开销还额外有CAS操作的开销。自旋锁减少不必要的CPU上下文切换。在轻量级锁升级为重量级锁时就使用了自旋加锁的方式锁粗化将多个连续的加锁、解锁操作连接在一起扩展成一个范围更大的锁。❝举个例子买门票进动物园。老师带一群小朋友去参观验票员如果知道他们是个集体就可以把他们看成一个整体锁租化一次性验票过而不需要一个个找他们验票。❞锁消除:虚拟机即时编译器在运行时对一些代码上要求同步但是被检测到不可能存在共享数据竞争的锁进行削除。有兴趣的朋友们可以看看我这篇文章Synchronized解析——如果你愿意一层一层剥开我的心[1]2. ThreadLocal原理使用注意点应用场景有哪些回答四个主要点ThreadLocal是什么?ThreadLocal原理ThreadLocal使用注意点ThreadLocal的应用场景ThreadLocal是什么?ThreadLocal即线程本地变量。如果你创建了一个ThreadLocal变量那么访问这个变量的每个线程都会有这个变量的一个本地拷贝多个线程操作这个变量的时候实际是操作自己本地内存里面的变量从而起到线程隔离的作用避免了线程安全问题。//创建一个ThreadLocal变量
static ThreadLocalString localVariable new ThreadLocal();
ThreadLocal原理ThreadLocal内存结构图由结构图是可以看出Thread对象中持有一个ThreadLocal.ThreadLocalMap的成员变量。ThreadLocalMap内部维护了Entry数组每个Entry代表一个完整的对象key是ThreadLocal本身value是ThreadLocal的泛型值。对照着几段关键源码来看更容易理解一点哈~public class Thread implements Runnable {//ThreadLocal.ThreadLocalMap是Thread的属性ThreadLocal.ThreadLocalMap threadLocals null;
}
ThreadLocal中的关键方法set()和get() public void set(T value) {Thread t Thread.currentThread(); //获取当前线程tThreadLocalMap map getMap(t); //根据当前线程获取到ThreadLocalMapif (map ! null)map.set(this, value); //KV设置到ThreadLocalMap中elsecreateMap(t, value); //创建一个新的ThreadLocalMap}public T get() {Thread t Thread.currentThread();//获取当前线程tThreadLocalMap map getMap(t);//根据当前线程获取到ThreadLocalMapif (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();}
ThreadLocalMap的Entry数组static class ThreadLocalMap {static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;}}
}
所以怎么回答「ThreadLocal的实现原理」如下最好是能结合以上结构图一起说明哈~❝Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals即每个线程都有一个属于自己的ThreadLocalMap。ThreadLocalMap内部维护着Entry数组每个Entry代表一个完整的对象key是ThreadLocal本身value是ThreadLocal的泛型值。每个线程在往ThreadLocal里设置值的时候都是往自己的ThreadLocalMap里存读也是以某个ThreadLocal作为引用在自己的map里找对应的key从而实现了线程隔离。❞ThreadLocal 内存泄露问题先看看一下的TreadLocal的引用示意图哈ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用如下❝弱引用只要垃圾回收机制一运行不管JVM的内存空间是否充足都会回收该对象占用的内存。❞弱引用比较容易被回收。因此如果ThreadLocalThreadLocalMap的Key被垃圾回收器回收了但是因为ThreadLocalMap生命周期和Thread是一样的它这时候如果不被回收就会出现这种情况ThreadLocalMap的key没了value还在这就会「造成了内存泄漏问题」。如何「解决内存泄漏问题」使用完ThreadLocal后及时调用remove()方法释放内存空间。ThreadLocal的应用场景数据库连接池会话管理中使用3. synchronized和ReentrantLock的区别我记得校招的时候这道面试题出现的频率还是挺高的~可以从锁的实现、功能特点、性能等几个维度去回答这个问题「锁的实现」 synchronized是Java语言的关键字基于JVM实现。而ReentrantLock是基于JDK的API层面实现的一般是lock()和unlock()方法配合try/finally 语句块来完成。「性能」 在JDK1.6锁优化以前synchronized的性能比ReenTrantLock差很多。但是JDK6开始增加了适应性自旋、锁消除等两者性能就差不多了。「功能特点」 ReentrantLock 比 synchronized 增加了一些高级功能如等待可中断、可实现公平锁、可实现选择性通知。❝ReentrantLock提供了一种能够中断等待锁的线程的机制通过lock.lockInterruptibly()来实现这个机制。ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。synchronized与wait()和notify()/notifyAll()方法结合实现等待/通知机制ReentrantLock类借助Condition接口与newCondition()方法实现。ReentrantLock需要手工声明来加锁和释放锁一般跟finally配合释放锁。而synchronized不用手动释放锁。❞4. 说说CountDownLatch与CyclicBarrier区别CountDownLatch一个或者多个线程等待其他多个线程完成某件事情之后才能执行;CyclicBarrier多个线程互相等待直到到达同一个同步点再继续一起执行。举个例子吧❝CountDownLatch假设老师跟同学约定周末在公园门口集合等人齐了再发门票。那么发门票这个主线程需要等各位同学都到齐多个其他线程都完成才能执行。CyclicBarrier:多名短跑运动员要开始田径比赛只有等所有运动员准备好裁判才会鸣枪开始这时候所有的运动员才会疾步如飞。❞5. Fork/Join框架的理解❝Fork/Join框架是Java7提供的一个用于并行执行任务的框架是一个把大任务分割成若干个小任务最终汇总每个小任务结果后得到大任务结果的框架。❞Fork/Join框架需要理解两个点「分而治之」和「工作窃取算法」。「分而治之」以上Fork/Join框架的定义就是分而治之思想的体现啦「工作窃取算法」把大任务拆分成小任务放到不同队列执行交由不同的线程分别执行时。有的线程优先把自己负责的任务执行完了其他线程还在慢慢悠悠处理自己的任务这时候为了充分提高效率就需要工作盗窃算法啦~工作盗窃算法就是「某个线程从其他队列中窃取任务进行执行的过程」。一般就是指做得快的线程盗窃线程抢慢的线程的任务来做同时为了减少锁竞争通常使用双端队列即快线程和慢线程各在一端。6. 为什么我们调用start()方法时会执行run()方法为什么我们不能直接调用run()方法看看Thread的start方法说明哈~ /*** Causes this thread to begin execution; the Java Virtual Machine* calls the coderun/code method of this thread.* p* The result is that two threads are running concurrently: the* current thread (which returns from the call to the* codestart/code method) and the other thread (which executes its* coderun/code method).* p* 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()方法其方法还是运行在主线程中没有起到多线程效果。7. CASCAS 有什么缺陷如何解决CAS,Compare and Swap比较并交换❝CAS 涉及3个操作数内存地址值V预期原值A新值B如果内存位置的值V与预期原A值相匹配就更新为新值B否则不更新❞CAS有什么缺陷「ABA 问题」❝并发环境下假设初始条件是A去修改数据时发现是A就会执行修改。但是看到的虽然是A中间可能发生了A变BB又变回A的情况。此时A已经非彼A数据即使成功修改也可能有问题。❞可以通过AtomicStampedReference「解决ABA问题」它一个带有标记的原子引用类通过控制变量值的版本来保证CAS的正确性。「循环时间长开销」❝自旋CAS如果一直循环执行一直不成功会给CPU带来非常大的执行开销。❞很多时候CAS思想体现是有个自旋次数的就是为了避开这个耗时问题~「只能保证一个变量的原子操作。」❝CAS 保证的是对一个变量执行操作的原子性如果对多个变量操作时CAS 目前无法直接保证操作的原子性的。❞可以通过这两个方式解决这个问题❝使用互斥锁来保证原子性将多个变量封装成对象通过AtomicReference来保证原子性。❞有兴趣的朋友可以看看我之前的这篇实战文章哈~CAS乐观锁解决并发问题的一次实践[2]9. 如何保证多线程下i 结果正确使用循环CAS实现i原子操作使用锁机制实现i原子操作使用synchronized实现i原子操作没有代码demo感觉是没有灵魂的~ 如下/*** 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 1000; i) {executorService.execute(() - {for (int j 0; j 2; 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 count1997
线程:pool-1-thread-1 count1998
线程:pool-1-thread-1 count1999
线程:pool-1-thread-2 count315
线程:pool-1-thread-2 count2000
最终结果是 2000
10. 如何检测死锁怎么预防死锁死锁四个必要条件死锁是指多个线程因竞争资源而造成的一种互相等待的僵局。如图感受一下「死锁的四个必要条件」互斥一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。占有且等待当一个进程在等待分配得到其他资源时其继续占有已分配得到的资源。非抢占不能强行抢占进程中已占有的资源。循环等待存在一个封闭的进程链使得每个资源至少占有此链中下一个进程所需要的一个资源。「如何预防死锁」加锁顺序线程按顺序办事加锁时限 线程请求所加上权限超时就放弃同时释放自己占有的锁死锁检测参考与感谢牛顿说我之所以看得远是因为我站在巨人的肩膀上~ 谢谢以下各位前辈哈~面试必问的CAS你懂了吗[3]Java多线程死锁[4]ReenTrantLock可重入锁和synchronized的区别总结[5]聊聊并发八——Fork/Join 框架介绍[6]Reference[1]Synchronized解析——如果你愿意一层一层剥开我的心: https://juejin.im/post/5d5374076fb9a06ac76da894#comment[2]CAS乐观锁解决并发问题的一次实践: https://juejin.im/post/5d0616ade51d457756536791[3]面试必问的CAS你懂了吗: https://blog.csdn.net/v123411739/article/details/79561458[4]Java多线程死锁: https://www.cnblogs.com/xiaoxi/p/8311034.html[5]ReenTrantLock可重入锁和synchronized的区别总结: https://blog.csdn.net/qq838642798/article/details/65441415[6]聊聊并发八——Fork/Join 框架介绍: https://www.infoq.cn/article/fork-join-introduction往期推荐
被问哭了一位小姐姐的阿里面经(附部分答案)不要一把梭了这才是SQL优化的正确姿势|原创干货关注下方二维码每一天都有干货