JUC部分面经整理

Synchronized这一套

1.锁中的synchronized和lock的区别?
(1)lock是一个接口,synchronized是java的内置语言实现的。
(2)Synchronized在发生异常的时候会自动释放锁,但是lock不会,需要手动释放。
(3)Lock在等待锁的过程中可以用interrupt打断,但是synchronized只能等待锁的释放,不能响应中断。
(4)Lock可以通过tryLock来看看是否获得锁,Synchronized不能知道是不是获得锁。
(5)用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了
(6)synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平
2.CAS的应用场景?
(1)对于资源竞争较少的情况下,synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态切换操作浪费额外的CPU;CAS基于硬件实现,不需要进入内核,无需切换线程,操作自旋几率较少,获得更高性能。
(2)资源竞争严重的时候,自旋几率较大,浪费资源。
3.乐观锁和悲观锁?
(1)乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。
(2)悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock。
4.对象实例的内存布局有什么构成?
(1)对象头
-对象标记MarkWord(hashcode,GC标记,GC次数,同步锁标记,偏向锁持有者)–(这个东西占了8个字节,类元信息占了8个字节,要是我们的对象没有一些实例信息的话,这个new出来的对象就占了16个字节,Hashcode不调用是不会生成的,调用才会有)
在这里插入图片描述
在这里插入图片描述
-类元信息(类型指针):指向方法区内的类元信息。
实际在这个里面是开启了压缩指针的,实际上开启或者是不开启压缩指针,只有对象头的话,都是占用16个字节,
-对象头多大(一般数组是有这个属性的)
在这里插入图片描述
-对象的分代年龄占用4个bit所以最大只能是15.
(2)实例数据:就是里面存放我们的数据的,
(3)对其填充
-这个一般会保证内存的填充为8个字节的倍数
5.为什么使用Synchronized是效率低下的?
(1)因为我们的监视器monitor(管城)是依赖底层的操作系统的mutex来实现的,所以性能很差,挂起线程和恢复线程是需要转化成内核态来完成,耗时耗力。
(2)这个管程里面有持有这个锁的线程,
6.Monitor和Java对象是如何关联的?
(1)如果一个Java对象被某个线程锁住,则该Java对象MarkWord字段中的LockWOrd会只想monitor的起始地址。
(2)这个monitor的owner字段会存放这个线程的id。
7.锁的分类以及差别?
(1)偏向锁,markword为偏向线程的id。
(2)轻量级锁,markword为栈中lockrecard的指针。
(3)重量级锁,markword为堆中monitor指针。
8.说一下偏向锁?
(1)偏向锁的默认code是101.无锁是001。
(2)当我们的临界代码是处于经常被同一线程调用,竞争较小的时候,就会被置成一个偏向锁,
(3)偏向锁就是把markword置换成持有该锁的线程的id,来了的线程只要一看这个markword是自己的线程id,那么便无需进行锁的竞争直接使用,减少了内核态到用户态的切换,减少消耗。
(4)一般偏向锁偏向第一个访问他的线程,新来的线程会先判断是不是自己的id是的话持有,不是的话cas替换,替换不成膨胀。
(5)偏向锁只有再其他人竞争的时候才会释放,否则不会释放。
(6)当我们的对象没有被任何synchronized持有的时候,也会有101,因为其实可偏向的。
9.偏向锁的撤销?
(1)当A线程正在同步块中执行代码的时候,B来竞争,这个时候A撤销偏向锁,升级成轻量锁,继续执行,B在外面CAS。
(2)当A线程执行完了同步代码块的时候,将对象头设置成无锁状态并撤销偏向锁重新偏向。
https://blog.csdn.net/Leon_Jinhai_Sun/article/details/111500752
10.什么是轻量级锁?
同一时间一般只有一个线程在竞争锁,一个进一个出,用CAS替代阻塞,尽量不使用阻塞。
11.竞争轻量级锁的过程?
(1)通过CAS来尝试获取偏向锁。
(2)成功的话,就使得偏向锁偏向新的线程。
(3)失败的话,偏向锁升级成轻量级锁,设置偏向锁标识为0,锁得标志位为00,此时轻量级锁由原偏向锁持有者持有。
12.LockWord?
JVM虚拟机会为每个线程的栈帧中创建存放锁记录的空间,若一个线程竞争锁的时候发现是轻量级锁,那么就会把锁的markword复制到自己的栈帧中的displaced markword 之后长是用cas将锁的markword替换成指向锁记录的指针。
https://blog.csdn.net/weixin_46017166/article/details/126255743
13.轻量级锁的释放?
释放锁的时候会用CAS将displaced markword里面的内容复制回锁对象的markword里面,但是要是失败了的话就说明有竞争,如果有其他的线程竞争导致了锁升级成为了重量级锁,那么CAS就会失败,释放锁并唤醒线程。
14.多少次自旋就会升级到重量级锁?
(1)Java6之前默认是10次或者自旋线程超过了CPU核数的一半。
(2)Java6之后,自旋成功了的话就认为下次还是比较容易,就给他多加几次自旋次数,要是失败了的话下次的次数就减少甚至不让他自旋。
15.偏向锁和轻量级锁的区别?
(1)偏向锁只有一个,轻量级锁有两个,争夺轻量级锁失败的线程会尝试自旋。
(2)轻量级锁每次退出同步块都会释放锁,偏向锁只有竞争发生的时候才会释放锁。
16.重量级锁的概念?
(1)由前62位作为指向Monitor对象的指针,在同步块之前加上monitor entry,后面加上monitor exit,成功得话就进入同步块并将monitor对象的owner中存放线程id。(10位重量级锁)
(2)重量级锁没有抢到的话就在同步队列里面等着,wait的也在等待队列里面等着,一旦notify了,就到同步队列里面等着,当entry exit 的时候就会通知同步队列里面的对象开始抢了。
17.锁升级之后hashcode去哪了?
(1)首先是偏向锁,hashcode和偏向锁是不能共存的,默认就是偏向锁,一旦对象调用hashcode方法的时候,偏向锁就会失效,直接变成轻量级锁。
(2)轻量级锁来说,其hashcode等markword存放在lock record中。
(3)重量级锁来说存放在monitor对象的objectmonitor中。
18.什么是锁消除?
锁消除就是当我们每一次调用同步代码块的时候,锁对象都不相同就相当于没有锁,就是锁消除。
19.什么是锁粗化?
多个同步代码块使用同一把锁在一个线程中,则实际上是没有用的,JIT就会将其变成一个同步代码块,减少获取和释放锁带来的负担。

AQS这一套

1.什么是AQS
通过state表示状态和一个双端队列。State用来表示变量的同步状态,内置的FIFO队列来完成资源获取的排队工作,将每条想要去抢占资源的线程封装成一个个Node,之后通过Node里面的waitStatus来表示状态,通过CAS来修改state状态。
在这里插入图片描述
2.waitStatus的几个状态?
0表示初始化的情况,1表示已经取消,-1表示需要唤醒后续节点,-2表示在condition中等待,-3表示广播状态:共享式的同步状态获取将被无条件的传播下去。
3.简单说一下lock流程?
(1)Sync继承了AQS,使用lock的时候,默认创建的是非公平锁,lock的时候,如果是非公平锁,会进行CAS,将state设置成1,之后acquire(1),要是公平锁的话,就直接acquire(1)。
(2)这个acquire里面会调用tryAcquire,分别在公平和非公平锁中有不同的实现。
(3)公平锁里面会先查看前面有没有节点。
公平锁的话,要是前面排队,他就到队列里面排队就好了;非公平得话,及时排头被唤醒了也不一定能抢到。
在这里插入图片描述
在这里插入图片描述
4.模拟一下t1,t2,t3线程抢占资源得时候出现得情况?
(1)t1线程首先进来cas尝试修改state,由0到1,由于没有线程争抢资源,所以顺利的修改完成,之后顺便把当前锁的持有者替换成当前线程。
在这里插入图片描述
(2)这时候t2线程也想要抢占资源,但是这个时候他也会cas进行抢占资源,但是会失败,因为t1线程已经占住了,所以他会走acquire(1)函数。
(3)之后进行三大流程,首先进行tryAcquire
在这里插入图片描述
(4)在这个里面,首先他会看看当前的state是不是0,如果是的话,就尝试cas一下看看能不能替换成功,成功了就把持有者设置为当前线程,失败了就返回false,如果当前锁的持有者就是当前线程,那么就相当于重入了一下,如果这两个都不对,那么就返回false。
在这里插入图片描述
(5)这个时候实际上就是都失败了,就需要进入addWaiter这个阶段了,先创建一个节点,这个节点包含着当前线程的id,由于这个时候我们的链表还是空的,所以pred也就是tail还是null,所以我们需要进入enq这个操作里面。
在这里插入图片描述
(6)在enq这个操作里面,我们首先看看tail是不是null,很明显是,我们cas创建一个新的空的node作为这个链表的head和tail,由于这个循环是一个死循环,所以我们又进入循环,这个时候,tail就不是null了,我们把当前节点的pre设置成tail(也就是head),之后再cas将我们的tail设置成当前节点,最后返回我们的链表的头节点。
(7)这样再这个函数里面,我们又能够将新建的这个node进行返回,这个node里面包含着我们当前想要抢占资源的这个线程。
在这里插入图片描述
(8)此时执行这个函数
在这里插入图片描述
在这里插入图片描述
首先先设置failed是true,之后获取node的前置,如果前置是head(前面的哑元)那么就尝试tryAcquire(因为还有可能同时来其他的线程想要竞争)一下,cas设置一下,要是成了的话,那么就把这个节点设置成头节点(即其线程属性为null,前驱也是null),之后原先head后继置也设置成null,这样就把原来的节点给彻底删除掉了,gc就可以了。
(9)但是要是前驱不是head的话,那说明他不是第一个节点,执行后续流程。
在这里插入图片描述
在这个函数里面看一下,当前node的前驱的waitStatus是不是-1.是的话就返回true,不是的话就尝试将其前驱节点的waitstatus设置成-1。最后返回false。
(10)之后把这个线程park住就可以了。
在这里插入图片描述
在这里插入图片描述
5.下面我们进入unlock流程
(1)首先我们进入一个tryRelease的函数,尝试去释放。
在这里插入图片描述
在这个函数里面,我们首先计算state减去1(arg)是不是为0,之后确定一下线程持有者是不是我们解锁的这个线程,如果是的话,就说明可以进行释放并将当前持有锁的县城设置成null并且将state设置成0.
在这里插入图片描述
函数执行到这,这个tryRelease就算成功了,之后进行unpark后面的节点。
在这里插入图片描述
在这里插入图片描述
Unpark之后,又回到之前park住那里继续循环,抢占,成功获取资源执行或者是再一次park住。
在这里插入图片描述

并发这一套

1.并发编程的三大特性?
(1)原子性:一个操作或者多个操作,要么全部执行成功,要么全部执行失败,中途不可被中断。
(2)可见性:多个线程共同访问共享变量,某个线程修改了此变量,其他线程能够立即看到修改后的值。
(3)有序性:程序执行的顺序按照代码的先后顺序执行,由于JMM模型允许编译器为了效率,进行指令的部分重排优化。
2.Synchronized如何保证三大特性?
(1)其是同步锁,同步块内的代码相当于某一时刻单线程执行,故不存在原子性和指令重排序的问题。
(2)线程解锁前,将共享变量的最新值刷到主存中。
(3)线程加锁前,将清空工作内存中共享变量的值,从主存中重新取值。
Volatile保证了可见性和有序性,不能保证原子性。
3.线程池的运行状态?
使用一个int来表示,高三位表示状态,低29位表示线程数,不能用两个int,因为要保证原子操作。
(1)正常使用111
(2)执行已提交不接受新任务000
(3)强制停止所有任务001
(4)任务执行完毕,活动现称为是010
(5)终止状态001
4.线程池的七大参数?
(1)核心线程数
(2)最大线程数:核心线程加上救急线程的总数。
来了任务创建线程执行任务。
线程数超过核心线程的时候就加入任务队列。
任务队列满了就创建救急线程执行任务。
执行完了的救急线程生命周期到了就自动销毁。
但是核心线程不用也在那放着。
(3)生存时间:针对的是救急线程。
(4)时间单位:针对救急线程
(5)任务队列
(6)线程工厂
(7)拒绝策略
5.线程默认的四个拒绝策略?
(1)拒绝抛异常
(2)拒绝不抛异常
(3)拒绝使用调用线程执行
(4)丢弃队列最老的任务之后重新提交任务
6.变量的可见性?
(1)Java内存模型的主要目标就是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的细节。
(2)JMM中规定所有的变量都存储在主内存中,每条线程都有自己的工作内存,线程的工作内存中保存了该线程所使用的变量的从主内存中拷贝的副本。线程对于变量的读、写都必须在工作内存中进行,而不能直接读、写主内存中的变量。同时,本线程的工作内存的变量也无法被其他线程直接访问,必须通过主内存完成。
(3)对于普通共享变量,线程A将变量修改后,体现在此线程的工作内存。在尚未同步到主内存时,若线程B使用此变量,从主内存中获取到的是修改前的值,便发生了共享变量值的不一致,也就是出现了线程的可见性问题。
7.volatile如何保证可见性和有序性?
(1)可见性:volatile强制所有的写操作都将最新的值刷新到内存中去,这会导致其余线程工作内存中的变量失效,想要用这个变量的话就必须要从内存中拉,这样就保证了变量的可见性。
(2)Volatile保证了其后面的操作不会被重排序到其前面。
8.什么是happes-before原则?
(1)unlock先于对同一个锁的lock操作
(2)一个线程内,书写于前面的代码先于后面的代码执行
(3)Volatile原则:对一个变量的写操作先于读操作。
(4)传递规则:A先于B,B先于C,A先于C。
(5)线程启动规则:Thread的start方法先于线程中的所有方法
(6)等等等。
9.死锁产生的条件?
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因为请求资源阻塞的时候,对已获取的资源保持不放。
(3)不剥夺条件:进程已经获得资源,在没有使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程形成一个首尾相接的循环等待资源关系。
10.死锁产生的原因?
(1)系统资源不足。
(2)进程运行推进的顺序不合适。
(3)资源分配不当。
11.如何解决死锁?
(1)最简单就是重启。
(2)撤销进程,终止参与死锁得进程,收回他们占有得资源(一次性撤销,或者一次性撤销。)
(3)进程回退。
12.使用线程池的好处?
(1)降低资源消耗:重复利用现有的线程来执行任务,避免多次创建和销毁线程。
(2)提高响应速度:省去了创建线程这个步骤,所以拿到任务时,可以立即开始执行。
(3)提供附加功能:线程池的可拓展性使得我们自己可以加入新的功能,比如定时,延时执行任务等等。
13.Java中线程同步的几种方式?
(1)使用wait和notify机制。
在这里插入图片描述
下面为具体操作
在这里插入图片描述
在这里插入图片描述
(2)使用Condition里面的await和signal方法
(3)通过一个阻塞队列
(4)使用countDownLatch()
https://blog.csdn.net/yoonerloop/article/details/81154596
14.Java的线程如何实现得?
(1)继承Thread类。
(2)实现runnable接口。
(3)实现callable接口。
(4)使用FutureTask去实现。
15.Java的线程和操作系统的线程的区别?
(1)操作系统中线程有五个状态:创建(线程创建好),就绪(线程等待CPU调度),运行,阻塞(线程阻塞住),停止。
(2)Java中线程有六个状态:创建,runnable(包括就绪,运行),waiting(调用join方法),timewaiting(调用sleep(n),wait(n),join(n)方法),blocked(Synchronized持有锁)。
16.什么时候会用多线程,线程池?
(1)涉及到异步任务的时候,或者需要有定时任务这种。
(2)线程池就是当我们需要频繁的创建和销毁线程的时候就需要使用线程池。
17.乐观锁可能产生的问题?
(1)CPU性能浪费较严重如果竞争激烈的话。
(2)不能保证代码块的原子性,如果同时替换多个变量。
(3)ABA问题。
18.CompletableFuture的优缺点?
(1)可以实现任务完成了主动通知。
(2)多个线程组合起来可能一个异步任务需要其他的异步任务的结果,所以需要将他们按照顺序进行组合起来。
(3)可以实现返回速度最快的。
19.CompletableFuture的四个静态方法?
(1)runAsync(接受runnable接口,没有返回值。
(2)supplyAsync(接受supply接口,有返回值)。
20.CompletableFuture里面的种种方法?
(1)whenComplete((v,e)->{});这个v表示的是上一个方法返回的值,后面e就是异常。
(2)其中get方法获取不到值得话抛出异常,join是不抛出异常得。getNow(XXX)这个是能获取结果得时候获取,获取不到得时候就返回XXX。
(3)thenApply:就是接受上一步得结果,之在下一步带者进行操作,依然有返回值。
(4)thenAccept:接受上一步得返回值,下一步带着进行操作,但是没有返回值了。
(5)thenRun:不接受上一步得返回值,下一步直接操作也没有返回值。
21.函数式接口的几种参数?
(1)Runnable:没有参数,没有返回值。
(2)Function:1个参数,有返回值。
(3)Consume:1个参数,没有返回值。
(4)BiConsume:2个参数,没有返回值。
(5)Supplier:无参数,有返回值。
22.synchronized里面的一些相关东东?
(1)其依靠的是monitorenter和monitorexit,monitorexit有两个,因为可能过程中会出现异常情况。
(2)每个obj都会对应一个monitor,之后每个monitor里面有一个owner。
(3)Waitset对应的是等待区间也就是wait后的状态的资源;entryset对应的是阻塞状态的。
23.死锁产生的原因?
(1)系统资源不足
(2)推进顺序不合适
(3)资源分配不当
–查看死锁?
Jps -l就是Java版本的ps,查看运行中的进程。
Jstack pid 看看是不是有死锁。
Jconsole就是主动查看死锁
24.Java中的interrupt?
Java中的线程状态应该由自己控制而不是其他的线程来进行控制。
(1)interrupt方法会将这个标志位设置成true,之后由我们的代码来进行控制看看是不是true,如果是true的话就进行打断。
(2)Interrupted方法是先看看状态,之后将状态进行复原,变成false。
(3)IsInterrupted,判断线程是不是被打断了。
(4)如果线程处于阻塞状态,sleep,wait,join的时候,那么调用interrupt会立即exit并且抛出异常,同时将interrupt标记设置成true。
25.线程等待唤醒的方式?
(1)wait和notify(Object 类中的,必须要持有锁才可以)。
(2)Await和signal需要在lock和unlock中间使用,也是需要持有锁的。
(3)Park和unpark,只需要成双成对的出现就可以了。
26.happens-before8原则?
(1)次序原则:一个线程内,前面的代码先于后面的代码执行。
(2)锁定原则:unlock的操作先于后序对同一个锁的lock操作。
(3)Volatile变量规则:volatile写操作先于后面的读操作,前面的写对于后面的读可见。
(4)传递规则:A先于B,B先于C,A先于C。
(5)线程启动原则:run的内容一定在start之后启动。
(6)线程中断原则:interrupt的调用一定先于检测功能的调用。
(7)线程终止原则:线程中所有操作都发生于检测终止之前。
(8)对象终结原则:对象初始化发生于finalize方法调用之前。
27.ThreadLocal的原理?
(1)每一个线程都有一个属性threadLoals,这个属性类型就是ThreadLocal的内部类ThreadLocalMap。
(2)这个map里面维护了一个个的Entry,其中的key就是ThreadLocal对象本身,value则是我们需要传递的值。
(3)当我们想要存储数据的时候,首先需要获取当前线程,之后获取当前线程对应的ThreadLocalMap,向里面set就可以了。
(4)同样的,get也是一样的。
(5)因为我们每个线程是自己独有的ThreadLocals属性,所以各个线程的threadLocalMap都是相护独立的,属性之间相护不影响。
(6)使用场景:数据分层传输,线程隔离,我的使用场景就是登录信息的存放。
28.ThreadLocal为什么会造成内存泄露?
(1)因为我们ThreadLocalMap的Entry是继承了WeakReference,也就是实际是一个弱引用,弱引用实际就是只要垃圾回收,就会将其回收。
(2)那么只要经过一次gc,就会把其回收,不过由于value还保持着一条引用链,就会导致即使用完了,还在里面存着。
(3)累积的多了就会出现内存泄漏的问题。
(4)解决方案就是用完了之后调用remove将这个value删除掉了即可。
29.Synchronized如何实现可重入?
就是基于计算机系统的mutex Lock实现的,当每一次加锁就将state加一,解锁就减一,最后到0了就是释放锁就可以了。
30.线程池中的阻塞队列的作用,为什么不直接创建线程帮助执行任务而是选择放入阻塞队列中?
(1)一般的队列就是一个定长的数组,超过了当前的长度就无法保留任务了;阻塞队列可以保留想要加入的任务,提供其等待直到队列可用的方法。
(2)创建新的线程需要一个全局锁,所有执行的线程都需要陷入阻塞,这样非常消耗性能。
31.线程池的原理?
(1)线程池将线程和其执行的任务解耦;
(2)之后会在线程中执行一个循环任务,有的话就调用方法执行,没有就进行空转。
32.线程池的核心线程数怎么设置?
(1)计算密集型,核心数+1;
(2)IO密集型,2N;
33.说一下MESI的内容?
(1)这四个字母分别表示一个状态。
-M:表示modify,就是当前缓存行得内容只属于当前CPU,和其他得CPU不是共享得,且已经被当前CPU修改过了,且需要同步到内存中。
-E:表示exclusive,就是当前缓存行得内容只属于当前CPU,且和内存中得数据是一样得,并且其他得CPU没有读这个缓存,只要是其他得CPU读了,那么就会变成S状态,如果当前CPU改了,那么就会变成M状态;
-S:表示shared,即当前缓存和内存中得数据是一致得,并且不单单属于一个CPU,其他得CPU也可以用,但是其他得CPU一旦改了,这个数据就会变成I;
-I:表示invalid无效得,即当前缓存是无效的,和内存中得数据是不一样得。
34.线程数过多会有什么问题?
(1)线程数过多首先会造成多个线程争抢资源的情况,浪费性能。
(2)过多的线程数导致多数线程在空闲,浪费资源。
35.ThreadLocal的泄漏原理?
(1)首先我们的当前线程中是存在一个threadLocalMap的,当我们创建ThreadLocal对象的时候,使用这个对象得引用作为key,去取值。
(2)如果在这里使用了强引用,那么我们当前线程只要不结束,永远持有这些对象就不会被回收,就会内存泄漏。
(3)使用弱引用的好处就是当我们gc的时候,弱引用就会被回收,之后在我们调用get,set,remove的时候就会找到key为null的去回收。
(4)还会有问题就是如果我们之后不调用,或者是static修饰的我们就需要主动删除,这样就还会一直持有不释放。
(5)使用弱引用只是多了一层保障。
35.ReentrantLock如何判断是谁持有的锁,子线程能不能直接持有父线程的锁?
根据线程id,不能直接持有。
36.为什么创建进程的代价比创建线程大?
(1)进程的创建需要分配一块完整的内存空间,大量的初始化操作,还要把内存分段(堆栈,正文区等)。
(2)线程的创建只需要一个PC计数器以及一个栈即可。
(3)进程里面的线程还可以复用。
37.线程和守护线程的区别?
(1)线程一般是我们执行任务的。
(2)守护线程一般是伴随线程而生存的,线程一旦死亡,守护线程也会结束。
38.死锁,活锁和饥饿是什么区别?
(1)死锁就是资源分配不合理:资源互斥,请求保持,不可剥夺,循环等待。
(2)活锁就是由于某些原因,请求资源的线程的某些条件一直不满足。
(3)饥饿因为某种原因一直获取不到(短进程优先)。
-高优先级线程持续占用低优先级线程的时间。
-线程被阻塞在一个等待进入同步块的状态。
39.什么是野线程?
使用new创建出来的线程就是野线程,不易于管理,且不容易扩展。
40.如何解决ABA问题?
实际上ABA问题就是因为一个数据,既是数据本身,同时也是用与比较的基础,即看他变不变才决定是否更新,可以考虑增加一个版本号来确定。
41.BlockingQueue的问题?
(1)提供三个方法,add是添加,添加失败会抛出异常;offer是添加,添加失败返回false;put是添加,添加失败会阻塞住。
(2)提供三个方法,poll是删除头部,删除失败返回null,否则返回元素;remove根据对象找到对应的元素,并删除,删除成功返回true,否则返回false;take为删除头部元素,队列位空那么就阻塞住,一直到有才删除。
42.说一下CountDownLautch和CyclicBarrier的区别?
(1)CountDownLautch是要求所有的线程都开始运行了,或者说都通过某一点之后了,才能做主线程的事情。(可以考虑库存,订单场景下的需要同步之后返回给用户信息,之后使用异步编排进行优化)
(2)CyclicBarrier则是所有的线程都需要准备好了,在同一时间开始运行。
43.什么是竞争条件?
当多个进程企图对某一个资源进行处理的时候,最终的结果又取决于对于进程的运行顺序的时候,这就发生了竞争条件。
44.JAVA虚拟机中的调度模型?
(1)分时调度:就是CPU轮流为每一个线程分配时间去做事情。
(2)抢占式调度:优先级高的先做,剩下的谁抢到了谁做。
45.如何优雅的打断线程?
(1)使用stop打断线程实际上是强制的停止线程,
(2)比如我们有一个监听策略,每两秒执行一次监听,监听完了睡眠两秒。
(3)我们执行了打断之后,会将标志置为true,之后再监听策略里面看一下是不是true,是的话,料理后事,之后停止操作。
(4)但是如果是在其sleep的时候打断的,那么就会抛异常并且将标志置为false,这时候我们捕捉异常之后将false改成true,就会在下一轮里面料理后事之后完成退出任务。
46.当一个线程进入某一个实例对象的synchronized方法的时候,其他的线程可不可以进入这个对象的其他方法?
可以进入的,只要这些方法不是被Synchronized修饰的就是可以的,但是一般一个线程安全的对象所有的方法都是线程安全的。
47.CopyOnWriteArrayList的特点?
(1)首先他会在写入的时候创建一个副本,即使副本被修改也会返回老版本的值。
(2)这种情况下要是数组比较大的时候,就可能出现OOM。
(3)实时性差一些,我们立即set一个数据但是我们可能读不到。
(4)其中蕴含了一些思想。
-读写分离。
-最终一致性。
-另外开辟空间的思路来解决并发冲突。
48.Java的wait,sleep有什么区别?
(1)wait会释放锁,sleep不会释放锁。
(2)Wait一般用于线程交互,sleep用于暂停执行。
(3)Wait会使线程进入waiting的状态,join也会;sleep(time),join(time),wait(time)都会进入timewaiting状态。Synchronized是进入blocked状态。
49.为什么wait,notify不在Thread里面呢?
首先因为Java中的锁实际上锁的是对象,而不是线程。
(1)我们不想阻塞住工作,比如我们要一个整数,但是线程A中没有,我们就需要等,这个时候我们想让他做别的事情,就把A挂起就好。
(2)这个工作就需要一个第三方的人来作,这个人就是我们的锁,也就是一个对象。
(3)所以无所谓我们的这个东西在线程上还是在对象上,只要提供了这个功能就可以。
(4)Java没有提供任何一个对象来提供锁和同步器的功能,这种设计能够使每一个类都能作为监控器来用于线程间的通信。
50.线程的submit和execute有什么区别?
(1)都是向线程池中提交任务。
(2)Submit是有返回值得,可以获取里面返回得结果;但是execute是没有返回值类型得。
51.votatile和atomic的区别?
(1)volatile主要是防止指令重排,并且内存可见性。
(2)但是这个东西不能保证原子性。
(3)Atomic是可以保证原子性的。
52.HashMapget方法获得的结果是null的时候代表了什么?
(1)可能是这个key对应的结果是null。
(2)可能是压根没有这个key,因为他是可以存放空值的。
53.如何避免死锁?
(1)破坏请求与保持条件:一次性申请所有资源。
(2)破坏不剥夺条件:申请不到可以释放主动释放持有资源。
(3)破坏循环等待:按照顺序申请资源。
(4)避免死锁:申请资源的时候借助算法对资源分配进行计算评估。
54.Synchronized和lock的区别?
(1)synchronized属于JVM层面,lock属于api层面的锁。
(2)Synchronized非公平的,lock可以公平也可以非公平。
(3)Synchronized不可中断,除非抛出异常;lock可以打断
(4)Synchronized不可以绑定多个条件,lock可以绑定多个条件。
55.AQS框架的几种实现?
(1)重要的东西就是state,这个东西是我们的标志。
(2)独占模式就是ReentrantLock,每次只允许一个线程执行,cas修改state为1,修改成功了,就执行,不成变成一个节点挂在队列后面,上面有waitStatus,0不算,-1为需要唤醒后继节点,-2为在Condition里面。这个执行完了,释放了,那么队列的第一个将和新来的线程竞争锁。(重入的时候state++)
(3)共享模式就是就CountDownLautch,多个线程执行就是state设置为N,线程数和state相等,之后park调用线程,执行一个线程就cas的方式state–,当所有的都执行完了,count减为0了,那么就unpark主线程。这个是等人到起了上课。
(4)CycliBarrier这个东西是找龙珠。
(5)Semaphore(信号量):允许多个线程同时访问,用用于控制线程的访问。抢车位和释放车位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值