大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析)

admin 3周前 (08-30) 科技 12 0

前言

小我私家珍藏的80道Java多线程/并发经典面试题,由于篇幅太长,现在先给出1-10的谜底剖析哈,后面一起完善,而且上传github哈~

https://github.com/whx123/JavaHome

民众号:捡田螺的小男孩

1. synchronized的实现原理以及锁优化?

synchronized的实现原理

  • synchronized作用于 方式或者 代码块,保证被修饰的代码在同一时间只能被一个线程接见。
  • synchronized修饰代码块时,JVM接纳 monitorenter、monitorexit两个指令来实现同步
  • synchronized修饰同步方式时,JVM接纳 ACC_SYNCHRONIZED符号符来实现同步
  • monitorenter、monitorexit或者ACC_SYNCHRONIZED都是 基于Monitor实现
  • 实例工具里有工具头,工具头内里有Mark Word,Mark 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 ThreadLocal<String> 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(); //获取当前线程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();
    }

ThreadLocalMap的Entry数组

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** 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的引用示意图哈,

大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第1张

ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用,如下 大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第2张

弱引用:只要垃圾接纳机制一运行,不管JVM的内存空间是否足够,都市接纳该工具占用的内存。

弱引用对照容易被接纳。因此,若是ThreadLocal(ThreadLocalMap的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:多个线程相互守候,直到到达同一个同步点,再继续一起执行。 大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第3张

举个例子吧:

  • CountDownLatch:假设先生跟同砚约定周末在公园门口聚集,等人齐了再发门票。那么,发门票(这个主线程),需要等列位同砚都到齐(多个其他线程都完成),才气执行。
  • CyclicBarrier:多名短跑运动员要最先田径比赛,只有等所有运动员准备好,裁判才会鸣枪最先,这时刻所有的运动员才会疾步如飞。

5. Fork/Join框架的明白

Fork/Join框架是Java7提供的一个用于并行执行义务的框架,是一个把大义务分割成若干个小义务,最终汇总每个小义务效果后获得大义务效果的框架。

Fork/Join框架需要明白两个点,分而治之事情窃取算法

分而治之

以上Fork/Join框架的界说,就是分而治之头脑的体现啦 大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第4张

事情窃取算法

把大义务拆分成小义务,放到差别行列执行,交由差别的线程划分执行时。有的线程优先把自己卖力的义务执行完了,其他线程还在逐步悠悠处置自己的义务,这时刻为了充实提高效率,就需要事情偷窃算法啦~

大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第5张

事情偷窃算法就是,某个线程从其他行列中窃取义务举行执行的历程。一样平常就是指做得快的线程(偷窃线程)抢慢的线程的义务来做,同时为了削减锁竞争,通常使用双端行列,即快线程和慢线程各在一端。

6. 为什么我们挪用start()方式时会执行run()方式,为什么我们不能直接挪用run()方式?

看看Thread的start方式说明哈~

    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</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
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</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. CAS?CAS 有什么缺陷,若何解决?

CAS,Compare and Swap,对照并交流;

CAS 涉及3个操作数,内存地址值V,预期原值A,新值B; 若是内存位置的值V与预期原A值相匹配,就更新为新值B,否则不更新

CAS有什么缺陷?

大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第6张

ABA 问题

并发环境下,假设初始条件是A,去修改数据时,发现是A就会执行修改。然则看到的虽然是A,中心可能发生了A变B,B又变回A的情形。此时A已经非彼A,数据纵然乐成修改,也可能有问题。

可以通过AtomicStampedReference解决ABA问题,它,一个带有符号的原子引用类,通过控制变量值的版原本保证CAS的准确性。

循环时间长开销

自旋CAS,若是一直循环执行,一直不乐成,会给CPU带来异常大的执行开销。

许多时刻,CAS头脑体现,是有个自旋次数的,就是为了避开这个耗时问题~

只能保证一个变量的原子操作。

CAS 保证的是对一个变量执行操作的原子性,若是对多个变量操作时,CAS 现在无法直接保证操作的原子性的。

可以通过这两个方式解决这个问题:

  • 使用互斥锁来保证原子性;
  • 将多个变量封装成工具,通过AtomicReference来保证原子性。

有兴趣的同伙可以看看我之前的这篇实战文章哈~ CAS乐观锁解决并发问题的一次实践[2]

9. 若何保证多线程下i++ 效果准确?

大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第7张
  • 使用循环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 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. 若何检测死锁?怎么预防死锁?死锁四个必要条件

死锁是指多个线程因竞争资源而造成的一种相互守候的僵局。如图感受一下: 大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第8张 死锁的四个必要条件:

  • 互斥:一次只有一个历程可以使用一个资源。其他历程不能接见已分配给其他历程的资源。
  • 占有且守候:当一个历程在守候分配获得其他资源时,其继续占有已分配获得的资源。
  • 非抢占:不能强行抢占历程中已占有的资源。
  • 循环守候:存在一个封锁的历程链,使得每个资源至少占有此链中下一个历程所需要的一个资源。

若何预防死锁?

  • 加锁顺序(线程按顺序做事)
  • 加锁时限 (线程请求所加上权限,超时就放弃,同时释放自己占有的锁)
  • 死锁检测

参考与谢谢

牛顿说,我之以是看得远,是由于我站在巨人的肩膀上~ 谢谢以下列位先辈哈~

  • 面试必问的CAS,你懂了吗? [3]
  • Java多线程:死锁 [4]
  • ReenTrantLock可重入锁(和synchronized的区别)总结 [5]
  • 聊聊并发(八)——Fork/Join 框架先容 [6]

小我私家民众号

大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析) 第9张
  • 以为写得好的小伙伴给个点赞+关注啦,谢谢~
  • 若是有写得不准确的地方,贫苦指出,感激涕零。
  • 同时异常期待小伙伴们能够关注我民众号,后面逐步推出更好的干货~嘻嘻
  • github地址:https://github.com/whx123/JavaHome

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

,

欧博Allbet

欢迎进入欧博Allbet官网(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

欧博开户声明:该文看法仅代表作者自己,与本平台无关。转载请注明:大发888游戏官网:小我私家珍藏的80道多线程并发面试题(1-10谜底剖析)

网友评论

  • (*)

最新评论

文章归档

站点信息

  • 文章总数:444
  • 页面总数:0
  • 分类总数:8
  • 标签总数:980
  • 评论总数:105
  • 浏览总数:2752