Java基础面试问题总结

阅读: 评论:0

Java基础面试问题总结

Java基础面试问题总结

1.ThreadLocal

线程本地变量,为了解决多线程访问时的并发问题。

应用场景:

1、线程不安全的对象每一个线程一个副本,例:日期格式化SimpleDateFormat。

2、同一链路上都需要使用或者传递的参数,例:用户登陆成功后的UserId可以存储到ThreaLocal种避免通过方法层层传递。Spring事物管理,将Connection存储到ThreadLocl中,避免层层传递对业务代码的侵入,同时也保证同一个事物中获取的Connection始终是同一个。

2.volatile的作用和原理

volatile是一种同步机制,比synchronized与Lock更轻量级。因为volatile关键字不会导致线程上下文的切换。

1、保证内容可见性、禁止指令重复排序、不保证原子性。

底层可见性是如何实现的?

  • 当对volatile变量执行写操作后,JMM会把工作内存中的最新变量值强制刷新到主内存。写操作会导致其他线程中的缓存无效(通过硬件层面的缓存一致性协议实现,常见的有MESI协议)

底层禁止指令重排序是如何实现的?

3.谈谈Java内存模型JMM

首先说一下java内存模型是什么?

它是一组规范(JSR-133)。全称是Java Memory Model,即JMM。用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果。定义了读写内存的规范,抽象出了主内存和本地内存的概念。

如下规定:

1、所有变量都存储在主内存当中,同时每个线程都有自己的工作内存,工作内存中的变量是主内存中的拷贝。

2、线程不能直接读写主内存中的变量,而是只能操作工作内存中的变量,然后同步到主内存中。

3、主内存是多线程共享的,但线程不能共享工作内存,如果线程间需要通信,需要通过主内存进行中转来完成。

主要针对:可见性、原子性、重排序这三个部分。

为了解决可见性的问题,提出了happens before原则:如果说一个操作happens before于另一个操作,那我们说第一个操作对于第二个操作是可见的。

具体包括哪些happens before原则:

1、单线程规则。

2、锁操作(synchronized和Lock)。

3、volatile关键字。

4、线程启动。

5、线程join。

6、传递性(如果事件A happens before B,B happens before C,那么A happens before C)。

7、中断(一个线程被中断那么检测它是否中断的线程一定可以看到)。

8、构造方法(构造方法的最后一条指令一定happens before于finalize()方法的第一条指令)。

9、JUC包中的操作。

为什么会存在可见性的问题?

CPU的硬件结构上讲,每个CPU都会有自己的内部缓存,分为L1、L2、L3,多有的计算操作都是在自己的本地内存中进行的。CPU之间的交互式通过主内存进行的。这就天然的会造成不同的cpu本地缓存的值会出现不一致的问题。这就是可见性问题。

原子性指的是什么?

原子性指的是一个操作是否可以分成多步去执行。

指令重排序的好处以及危害?

可以增加运行的速度,例如:a=1,b=2,a=a+1对应的汇编指令

load a ,set a to 1,store a

load b ,set b to 2,store b

load a ,set a to 2,store a

一共是9条汇编指令。

重排序之后:

load a ,set a to 1,set a to 2,store a

load b ,set b to 2,store b

一共7条指令。

重排序在单线程情况下通常是没有什么意想不到的情况,因为编译器和CPU指令在执行时都会遵循as if serial(不管怎么重排序单线程的执行结果不能被改变。),但是在多线程情况下会存在可见性的问题。

3.J.U.C是什么?

JUC是urrent包的简称,自jdk1.5之后添加。并发流程控制的工具类。

4.JUC中有哪些常用的工具类,以及使用场景?

1、Atomic包。

AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

  当有多个Atomic变量时可以通过定义Atomic*Array来使用,例:
 

AtomicIntegerArray atomicIntegerArray3 = new AtomicIntegerArray(10); 
// 设置下标5位置的值为30,当前值为10
Boolean bool = atomicIntegerArray3pareAndSet(5,10,30);
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater(只有极少数情况下需要线程安全的去访问与修改变量时使用,平时不需要修改,只有某一个时间断可能存在多线程修改的情况时使用)
AtomicMarkableReference、AtomicStampedReference(解决ABA问题用)
LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator

(每个线程中保存一个副本,最后加和,利用的是ThreadLocal)

2、Lock包。

ReentrantLock
private final ReentrantLock lock = new ReentrantLock();
public void m() {lock.lock();  // block until condition holdstry {// ... method body} finally {lock.unlock();}
}
ReentrantReadWriteLock
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
ReentrantReadWriteLock.ReadLock readLock = adLock();
LockSupport

JDK1.5开始提供,可以任意时刻去控制线程的状态,阻塞或者唤醒。

// 阻塞线程
LockSupport.part(thread);
// 唤醒线程
LockSupport.unpart(thread);

3、并发安全的容器

ConcurrentHashMap、ConcurrentSkipListMap并发安全的MapArrayBlockingQueue、LinkedBlockingQueue、LinkedBlockingDeque、PriorityBlockingQueue、ConcurrentLinkedQueue、DelayQueue、CopyOnWriteArrayList

4、并发流程控制

CountDownLatch、CyclicBarrier、Semaphore、Exchanger、Phaser
import java.util.Random;
import urrent.CountDownLatch;/*** 等待N个任务都执行完成后在执行某一个任务*/
public class CountDownLatchTest {private static int COUNT = 10;private static CountDownLatch countDownLatch = new CountDownLatch(COUNT);public static void main(String[] args) throws InterruptedException {for(int i = 0;i < COUNT;i++){int cpi = i;Thread t = new Thread(() -> {try {Random random = new Random();int seconds = Int(10);Thread.sleep(seconds * 1000);System.out.println("thread: + " + cpi + "执行完成,耗时: " + seconds + "秒");} catch (InterruptedException e) {throw new RuntimeException(e);}untDown();});t.start();}countDownLatch.await();System.out.println("等待所有线程执行完成,主线程开始执行");}
}thread: + 8执行完成,耗时: 1秒
thread: + 3执行完成,耗时: 1秒
thread: + 7执行完成,耗时: 2秒
thread: + 1执行完成,耗时: 2秒
thread: + 5执行完成,耗时: 4秒
thread: + 6执行完成,耗时: 6秒
thread: + 2执行完成,耗时: 7秒
thread: + 4执行完成,耗时: 8秒
thread: + 0执行完成,耗时: 9秒
thread: + 9执行完成,耗时: 9秒
等待所有线程执行完成,主线程开始执行
import java.util.Random;
import urrent.BrokenBarrierException;
import urrent.CyclicBarrier;/*** 更倾向于等待N个任务达到某一个时间点上N个任务同时执行*/
public class CyclicBarrierTest {private static int COUNT = 10;private static CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT);public static void main(String[] args) throws InterruptedException {for(int i = 0;i < COUNT;i++){int cpi = i;Thread t = new Thread(() -> {try {Random random = new Random();int seconds = Int(10);Thread.sleep(seconds * 1000);System.out.println("thread: + " + cpi + "执行完成,耗时: " + seconds + "秒");cyclicBarrier.await();System.out.println("凑齐: " + COUNT + "开始同时执行");} catch (InterruptedException e) {throw new RuntimeException(e);} catch (BrokenBarrierException e) {throw new RuntimeException(e);}});t.start();}}
}thread: + 9执行完成,耗时: 0秒
thread: + 3执行完成,耗时: 1秒
thread: + 5执行完成,耗时: 1秒
thread: + 4执行完成,耗时: 1秒
thread: + 2执行完成,耗时: 3秒
thread: + 6执行完成,耗时: 4秒
thread: + 7执行完成,耗时: 5秒
thread: + 0执行完成,耗时: 6秒
thread: + 8执行完成,耗时: 6秒
thread: + 1执行完成,耗时: 7秒
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行
凑齐: 10开始同时执行

 Countdownlatch与CyclicBarrier看起来功能非常类似,但是其主要的区别是CountdownLatch是基于事件的,CyclicBarrier是基于线程的。同时, Countdownlatch只能使用一次,CyclicBarrier可以循环使用。

import urrent.Semaphore;
import urrent.atomic.AtomicInteger;/*** 用来控制任务同时执行的数量*/
public class SemaphoreTest implements Runnable{private static AtomicInteger DO_SOMETHING_COUNT = new AtomicInteger();private static int THREAD_COUNT = 10;private static Semaphore semaphore = new Semaphore(2);public static void main(String[] args) {SemaphoreTest semaphoreTest = new SemaphoreTest();for(int i = 0;i <  THREAD_COUNT;i++){Thread t = new Thread(semaphoreTest);t.start();}}@Overridepublic void run() {try {semaphore.acquire();doSomeThing();lease();} catch (InterruptedException e) {throw new RuntimeException(e);}}private void doSomeThing(){DO_SOMETHING_COUNT.incrementAndGet();int seconds = 1;try {Thread.sleep( seconds * 1000);System.out.println("thread: + " + Thread.currentThread().getName() + "做了某些事情,耗时: " + seconds + "秒,DO_SOMETHING_COUNT = " + DO_());DO_SOMETHING_COUNT.decrementAndGet();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}thread: + Thread-0做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-1做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-2做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-3做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-5做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-4做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-6做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-7做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-8做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 2
thread: + Thread-9做了某些事情,耗时: 1秒,DO_SOMETHING_COUNT = 1
import java.util.Random;
import urrent.Exchanger;/*** 用于两个线程之间的数据传递,如果线程A到达了指定的交换点,* 线程B还没有到达,此时线程A会等待线程B也到达指定的数据交换点。*/
public class ExchangerTest {private static Exchanger<String> exchanger = new Exchanger();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {Random random = new Random();int seconds = Int(10);Thread.sleep(seconds * 1000);System.out.println("thread1 执行完成,耗时: " + seconds + "秒,准备交换数据");String v = hange("t1");System.out.println("thread1 获取到的 v = " + v);} catch (InterruptedException e) {throw new RuntimeException(e);}});t1.start();Thread t2 = new Thread(() -> {try {Random random = new Random();int seconds = Int(10);Thread.sleep(seconds * 1000);System.out.println("thread2 执行完成,耗时: " + seconds + "秒,准备交换数据");String v = hange("t2");System.out.println("thread2 获取到的 v = " + v);} catch (InterruptedException e) {throw new RuntimeException(e);}});t2.start();// 主线程等待t1、t2线程执行完成t1.join();t2.join();}
}thread1 执行完成,耗时: 6秒,准备交换数据
thread2 执行完成,耗时: 9秒,准备交换数据
thread2 获取到的 v = t1
thread1 获取到的 v = t2

Phaser jdk1.7引入,Phaser可以认是CyclicBarrier与CountDownLatch的整合,可以用考试做类比,一个班级进行考试必须等所有同学(这里的同学做题可以看做不同的线程在执行任务)答完题,才能进入下一科考试

5.HashMap的扩容

hashMap的初始长度为16(2^n),当元素的个数大于 size * 拓容因子(默认0.75) 时进行扩容。

为什么是0.75?

官方文档给的解释是合适的值,可以大致分析原因,如果拓容因子定义为1会增加hash碰撞,拓容因子定义0.5会降低空间的使用率。


6.Java中的原子操作有哪些?

1、基本类型的赋值操作,原则上需要排除long和double类型,但是商用的JVM已经对这两种类型的赋值操作进行处理,保证其原子性。

long和double理论上赋值并非原子性操作,long和double的长度都是64位占用8个字节,在32位的操作系统上需要分成两步进行操作,所以理论上不是原子操作。

2、引用类型的赋值操作。

3、Atomic包中的类操作。

7、JVM运行时数据区域。

8.Java中的引用分为哪几种?为什么要区分?

分为四种:

1、Strong Reference(强引用)。

只要强引用存在垃圾收集器就永远不会回收被引用的对象。

2、Soft Reference(软引用)。

用来描述有用,但是非必需的对象。在系统将要发生内存溢出之前,会讲此类对象列入回收范围之内进行二次回收。二次回收之后仍然没有可用的内存,才会报出内存溢出的异常。

3、Weak Reference(弱引用)。

也是用来描述非必需的对象,比Soft Reference还要弱一些。被弱引用关联的对象,只能生存到下一次垃圾回收之前。当垃圾收集器开始工作时,无论当前内存是否充足都会被回收。

4、Phantom Reference(虚引用)。

最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存周期的影响,也不可以通过虚引用来获取对象实例。为对象设置一个虚引用的唯一目的是在其被回收时收到一个系统通知。

为什么需要这几种引用?

如果只有一种引用关系,是非黑即白。要么有用要么没用。我们希望描述这一类对象:如果在内存充足的时候可以保留,内存紧张的时候回收,此时就无能为力了。所以引入了这四种引用关系。

9.谈谈垃圾回收?

垃圾回收并不是Java语言独有的概念,而是所有带有自动回收内存功能的高级语言都存在的(java、go、c#...)。

垃圾回收都需要考虑的问题:

1、哪些内存需要回收?

2、什么时候回收?

3、如何回收?

确定哪些内存需要回收常用的有引用计数法、可达性分析法。Java采用的是可达性分析法,引用计数法的缺点是无法处理循环引用的问题。可达性分析法需要确定GCRoot。

Java中GCRoot包括以下四种:

1、虚拟机栈中引用的对象。

2、本地方法栈(JNI)中引用的对象。

3、方法区中类的静态属性引用的对象。

4、方法区中常量引用的对象。

垃圾回收算法:

1、标记清除算法。

最大的问题是会产生严重的内存碎片。

2、复制算法。

基于标记清除算法进行的改进,将可使用的内存空间平分为两块。每次都是用其中的一块,当内存用完时将存活的对象拷贝到另一块。如果只是这样的话会大大降低内存的使用率。每次只能使用一半的内存,所以在此基础上进行了优化。IBM进行过研究百分之98的对象都是“朝生夕死”。所以并不需要1:1的来分配内存空间。而是将内存分为一块较大的空间Eden和两块较小的Survivor区域。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8∶1,也就是每次新生代中可用内存空间为整个新生代容量的90% (80%+10%),只有10%的内存会被“浪费”。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。

  

3、标记-整理算法

复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

4、分代收集算法

当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。 

Java中常见的垃圾收集器:

新生代:Serial、ParNew、Parallel Scavenge。

老生代:CMS、Serial Old、Parallel Old。

以及新生代和老生代都可以使用的G1。

直到现在为止还没有最好的收集器出现,更加没有万能的收集器,所以我们选择的只是对具体应用最合适的收集器。

Serial 

Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。采用复制算法。

应用场景:客户端应用的默认垃圾收集器。

ParNew 

ParNew收集器其实就是Serial收集器的多线程版本。在早期的JDK版本中,它是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。

Parallel Scavenge

Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器。

与ParNew的区别:

1、它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。它也经常称为“吞吐量优先”收集器。

吞吐量 = 运行用户代码时间 /(运行用户代码时间 +垃圾收集时间)

2、增加自适应调节策略。

Serial Old

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。

应用场景同样是客户端。

Parallel Old

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

这个收集器是在JDK 1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的状态。原因是,如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old(PS MarkSweep)收集器外别无选择(还记得上面说过Parallel Scavenge收集器无法与CMS收集器配合工作吗?)。由于老年代Serial Old收集器在服务端应用性能上的“拖累”,使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大化的效果,由于单线程的老年代收集中无法充分利用服务器多CPU的处理能力,在老年代很大而且硬件比较高级的环境中,这种组合的吞吐量甚至还不一定有ParNew加CMS的组合“给力”。

直到Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

CMS

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。

CMS收集器是基于“标记—清除”算法实现的,它的运作过程相对于前面几种收集器来说更复杂一些,整个过程分为4个步骤,包括:

1、初始标记(STW),枚举GCRoots。

2、并发标记。

3、重新标记(STW)。

4、并发清除。

CMS收集器的缺点:

1、对CPU资源非常敏感。其实,面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。

2、CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。

3、基于“标记—清除”算法实现会导致内存碎片化。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。

10.索引创建、使用原则

1、对经常被作为查询条件的字段建立索引。

2、对经常关联、排序、分组的字段建立索引。

3、不要建立过多的索引:索引本身需要占用空间;索引过多时会增加修改、删除的处理负担。最多不应超过5个。

4、字段内容区分度不大时不建议创建索引,考虑与其他字段建立符合索引。

5、索引字段不应过长,增加索引占用的空间以及索引查询效率。

6、符合索引使用时应遵循最左原则。

7、查询时索引列不参与计算,例如:

select * from test where DATE_FORMAT('create_time','%Y-%m-%d') = '2022-10-22' 
# 修改为
select * from test where create_time between '2022-10-22 00:00:00' and '2022-10-22 23:59:59'

11.实现线程安全的方法

1、互斥同步,也就是锁,synchronized关键字、JUC工具包中的LOCK。

2、非阻塞同步也就是乐观锁。CAS操作。

3、无同步方案,要保证线程安全,并不是一定就要进行同步,两者没有因果关系。可重入代码(Reentrant Code)、线程本地存储(Thread Local Storage)。

可重入代码这种代码也叫做纯代码(Pure Code),可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。

8.Java内存空间是怎么分配的?

主要是在堆上分配,如果启用了本地线程分配缓冲,则优先在TLAB(Thread Local Allocation Buffer)。具体的细节上有所差异与使用不同的垃圾回收器和参数配置有关系。

以Serial 和 Serial Old垃圾收集器为例,对象默认是在新生代上分配内存。经历过15次(经历次数可配置)Minor Gc后会将其转移到老年代。大对象直接回被分配到老年代(对象大小可配置)。

Minor Gc触发的条件:新生代内存不够用时。

Full Gc触发的条件:老年代空间不足、方法区空间不足、YGC时发现可能回导致担保失败。

在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

12.G1收集器

13.BIO、NIO、AIO对比

谈到io模型首先要说一下linux操作系统的几种io模型:

  • 阻塞式I/O(Blocking I/O)
  • 非阻塞式I/O (Nonblocking I/O)
  • I/O复用 (I/O multiplexing)
  • 信号驱动式I/O(SIGIO)
  • 异步IO(POSIX的aio_系列函数)

BIO 早期的java只有一种阻塞式的io模型,必须等待所有数据读完线程才会继续执行,阻塞针对的是线程。这样就会导致一个问题,拿web服务举例子,此模型一个线程处理一个请求,因此在高并发的场景下此模型就会有严重的性能问题。

NIO 在jdk1.4 java引入了Nio模型,是基于linux操作系统的I/O复用 (I/O multiplexing)实现,此模型的好处就是,不在需要一个链接对应一个线程,可以通过轮询的方式来获取哪些链接有新的数据进来,用尽可能少的线程来处理更多的链接。减少线程切换带来的额外的性能开销。

AIO 是基于异步IO(POSIX的aio_系列函数)实现,完全通过异步回调的方式实现。

14.AQS是什么

AQS全称是AbstractQueuedSynchronizer,是同步控制器的开发框架。基于它实现的同步控制器有:CountDownLatch、CyclicBarrier、Semaphore、ReentrantLock等。

它有主要的三个要素:

1.state 需要控制资源。

2.一个线程安全的先进先出的队列,CLH队列(CLH是三个人的名字首字母),头节点删除、尾部节点插入,插入是通过CAS操作去争抢更新尾部节点的引用。

 3.期望协作工具类去实现的重要方法获取/释放。

本文发布于:2024-01-31 04:18:33,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170664591625402.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:上海java面试
标签:基础   Java
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23