面经2020.2.29

1、自我介绍

 

2、SpringBoot底层如何运行的?SpringBoot的监听器?

 

3、项目中遇到的问题及解决?

 

4、数据库两大引擎?

Innodb引擎

Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。它本身实际上是基于Mysql后台的完整的系统。Mysql运行的时候,Innodb会在内存中建立缓冲池,用于缓冲数据和索引。但是,该引擎是不支持全文搜索的。同时,启动也比较的慢,它是不会保存表的行数的。当进行Select count(*) from table指令的时候,需要进行扫描全表。所以 当需要使用数据库的事务时,该引擎就是首选。由于锁的粒度小,写操作是不会锁定全表的。所以在并发度较高的场景下使用会提升效率的。

MyIASM引擎

是MySql的默认引擎,但不提供事务的支持,也不支持行级锁和外键。因此当执行Insert插入和Update更新语句时,即执行写操作的时候需要锁定这个表。所以会导致效率会降低。不过和Innodb不同的是,MyIASM引擎是保存了表的行数,于是当进行Select count(*) from table语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以, 如果表的读操作远远多于写操作时,并且不需要事务的支持的。可以将MyIASM作为数据库引擎的首先。
 

5、并发包下的有哪些类?

ConcurrentMap

ConcurrentMap接口下有俩个重要的实现 :ConcurrentHashMap,ConcurrentskipListMap (支持并发排序功能)
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。在多线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好。

CountDownLatch

CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,可以利用CountDownLatch来实现这种功能了

//先创建对象
CountDownLatch countDownLatch=new CountDownLatch(5)
 
//开启线程后,调用当前方法主线程阻塞  countDown结果为0, 阻塞变为运行状态
countDownLatch.await();
 
CyclicBarrier

CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。 

 CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 

CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。

Semaphore

Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。它的用法如下:

availablePermits函数用来获取当前可用的资源数量

wc.acquire(); //申请资源

wc.release();// 释放资源

并发队列

在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue。

ConcurrentLinkedDeque
ConcurrentLinkedQueue : 是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue.它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素。
ConcurrentLinkedQueue重要方法:
add 和offer() 都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别)
poll() 和peek() 都是取头元素节点,区别在于前者会删除元素,后者不会。

BlockingQueue

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:

①、在队列为空时,获取元素的线程会等待队列变为非空。
②、当队列满时,存储元素的线程会等待队列可用。 

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

ArrayBlockingQueue

ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。

ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。下面

LinkedBlockingQueue

LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。

和ArrayBlockingQueue一样,LinkedBlockingQueue 也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。

PriorityBlockingQueue

PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样。需要注

意,PriorityBlockingQueue中允许插入null对象。

所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口,队列优先级的排序规则就

是按照我们对这个接口的实现来定义的。

另外,我们可以从PriorityBlockingQueue获得一个迭代器Iterator,但这个迭代器并不保证按照优先级顺

序进行迭代。

SynchronousQueue

SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。
 

6、为啥说NIO是非阻塞IO?说说线程池有哪些?有哪些参数?队列有什么作用?

NIO是基于IO多路复用模型,多路复用本质是同步IO,其需要调用方在读写事件就绪时主动去进行读写。在NIO中,通过selector来获取就绪的事件,当selector上监听的channel中没有就绪的读写时间时,其可以直接返回,或者设置一段超时后返回。可以看出NIO可以实现非则塞,而不像传统IO里必须则塞当前线程直到可读或可写。

newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
 

一、corePoolSize 线程池核心线程大小

线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会 被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

二、maximumPoolSize 线程池最大线程数量

一个任务被提交到线程池后,首先会缓存到工作队列(后面会介绍)中,如果工作队列满了,则会创建一个新线程,然后从工作队列中的取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize来指定。

三、keepAliveTime 空闲线程存活时间

一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

四、unit 空间线程存活时间单位

keepAliveTime的计量单位

五、workQueue 工作队列

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

①ArrayBlockingQueue

基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

②LinkedBlockingQuene

基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

③SynchronousQuene

一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

④PriorityBlockingQueue

具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

六、threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

七、handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

①CallerRunsPolicy

该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。

②AbortPolicy

该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

③DiscardPolicy

该策略下,直接丢弃任务,什么都不做。

④DiscardOldestPolicy

该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

 

7、静态关键字static用法?

被static修饰的成员变量叫做静态变量,也叫做类变量,说明这个变量是属于这个类的,而不是属于是对象,没有被static修饰的成员变量叫做实例变量,说明这个变量是属于某个具体的对象的。

静态变量和实例变量的区别

实例变量:每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。

静态变量:静态变量由于不属于任何实例对象,是属于类的,所以在内存中只会有一份,在类的加载过程中,JVM为静态变量分配一次内存空间。

被static修饰的方法也叫做静态方法,因为对于静态方法来说是不属于任何实例对象的,那么就是说在静态方法内部是不能使用this的,因为既然不属于任何对象,那么就更谈不上this了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值