最近在B站上制作了关于java面试的视频,希望各位大佬可以给点指导意见,作为一个老学姐,我认为有责任把知识分享给大家
【Java学姐】SQL调优必备Explain查看执行计划各项参数解析:https://www.bilibili.com/video/BV1iZ4y1j7Wp/
【Java面试必问】Mysql索引/存储引擎/行表锁/分库分表/主从复制:https://www.bilibili.com/video/BV1DK4y1C7o7/
【Java学姐】8分钟搞懂MySQL为什么用B+树做索引:https://www.bilibili.com/video/BV1Ka4y1t7ev/
【Java面试必问】Redis持久化/复制/雪崩/击穿/双写一致性/过期策略:https://www.bilibili.com/video/BV1E7411S7Dh/
【JAVA学姐】10分钟学会如何查看生产服务器各项状态指标: https://www.bilibili.com/video/BV1s7411D7zE/
【Java面试必问】GC垃圾回收算法: https://www.bilibili.com/video/BV1H7411Q7M8/
【Java面试必问】JVM调优参数解析及Java虚拟机内存模型: https://www.bilibili.com/video/BV1G7411Q7Hz/
【Java面试必问】【多线程开发必用JUC】Java并发包-JUC: https://www.bilibili.com/video/BV14E411F7qS/
【Java面试必问】学姐带你学面试急救包-基础讲解:https://www.bilibili.com/video/BV1LE411F79v/
注:为节约大家的时间,可以开启1.5倍速观看。
==========以下是jvm部分笔记,可观看视频讲解=========
5、java锁
①公平锁:队列先来后到 new ReentrantLock(true);
②非公平锁 :可以插队(可造成优先级反转和饥饿的现象) new ReentrantLock();
③可重入锁(递归锁):ReentrantLock/Syncronized 类似于大门钥匙---防止死锁
同一线程外层函数获取锁后,内层递归函数仍然能获取该锁(内层会自动获取锁)
即:线程可以进入任何一个它已经拥有的锁所同步着的代码块
④自旋锁:尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,好处:减少线程上下文切换的消耗,缺点:循环会消耗CPU
⑤独占锁(写):该锁一次只能被一个线程所持有。ReentrantLock/Syncronized
⑥共享锁(读):该锁可被多个线程所持有。
ReentrantReadWriteLock:读锁是共享锁,写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。
写操作:原子+独占
6、线程排序常用锁
①CountDownLatch:秦灭六国一统华夏,线程减到0才执行主线程(减法)
②CyclicBarrier:集齐7颗龙珠,可以召唤神龙(加法)
③Semaphore:信号灯,多个线程抢多份资源。示例:争车位
作用:一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制
7、阻塞队列:可以使拥挤的线程进行等待,避免失败率(自己理解)代码
多线程领域的阻塞:在某些情况下会挂起线程(阻塞),一旦条件满足,被挂起的线程又会自动被唤醒
①阻塞队列有没有好的一面:BlockingQueue可以不用关心阻塞和唤醒,BlockingQueue全包
②不得不阻塞,你如何管理:
当阻塞队列是空时,从队列中获取元素的操作会被阻塞
当阻塞队列是满时,往队列里添加元素的操作会被阻塞
以上可以理解为蛋糕店成产蛋糕,蛋糕柜空时,消费者阻塞;当蛋糕柜满时,生产者阻塞
Collection的实现类有List和Queue
③Queue的实现类有:BlockingQueue接口
<1>ArrayBlockingQueue:由数组结构组成的有界阻塞队列
<2>LinkedBlockingQueue:由链表结构组成的有界(大小默认Integer.MAX_VALUE)阻塞队列
<3>PriorityBlockingQueue:支持优先级排序的无界阻塞队列
<4>DelayQueue:使用优先级队列实现的延迟无界阻塞队列
<5>SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
<6>LinkedTransferQueue:由链表结构组成的无界阻塞队列
<7>LinkedBlockingDeque:由链表结构组成的双向阻塞队列
④阻塞队列的核心方法:ArrayBlockingQueue
<1>异常:add(e),remove(),element()
<2>特殊值:offer(e) ,poll(),peek()--- 成功true,失败false
<3>阻塞:put(e),take()
<4>超时退出:offer(e,time,unit),poll(time,unit)
⑤阻塞队列用在哪里?
Syncronized-ReentrantLock
【注】多线程企业级模板口诀:线程操作资源类,判断-干活-唤醒通知,严防多线程状态下的虚假唤醒
<1>生产者消费者模式:
<2>线程池:
<3>消息中间件:生产一个,消费一个
⑥Syncronized-ReentrantLock区别
Syncronized:JVM层面,java关键字,底层monitor,不需要手动释放,
不可中断,除非抛出异常或正常运行完成,非公平锁,要么唤醒一个线程,要么唤醒全部线程
ReentrantLock:api层面,类,需要手动释放,可中断,可设置超时方法也可在代码块中调用interrupt方法,默认非公平锁,构造方法传true为公平锁,可以用来实现分组唤醒需要唤醒的线程们(精确唤醒)
练习:ABC三个线程,A打印5次,B打印10次,C打印15次,循环3次,按顺序
8、线程池
<1>为什么要用线程池?
线程池主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他宪曾执行完毕,再从队列中取出任务来执行。
主要特点:线程复用,控制最大并发数,管理线程
<2>创建线程的四种方式:
继承Thread类
实现Runnable接口:无返回值,不抛异常,实现run方法
实现Callable接口:有返回值,会抛异常,实现call方法
通过线程池
①callable
<3>线程池框架:底层ThreadPoolExecutor
①ExecutorService threadPool = Executors.new FixedThreadPool(int)-一池固定线程数
②Executors.newSingleThreadExecutor()-一池一个线程
③Executors.newCachedThreadPool()可扩容的一池多线程
上述三种工作中用哪种?一个都不用,用ThreadPoolExecutor自己创建,工具类封装好的有界队列长度过大
<4>线程池的7大重要参数
①corePoolSize:线程池中的核心线程数(类似于银行网点的窗口数)
②maximumPoolSizse:线程池能够容纳同时执行的最大线程数(类似于银行窗口开放数)
③keepAliveTime:多余的空闲线程的存活时间
④unit:keepAliveTime的单位
⑤workQueue:任务队列,被提交但尚未被执行的任务(阻塞队列)
⑥threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可
⑦handler:决绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数
<5>线程池的底层工作原理
比如银行办公窗口满了,阻塞队列(候客区)也满了,这个时候需要扩容银行的加班窗口,扩容后新进来的会直接抢占新扩容的加班窗口,若任务还在持续增加会启动饱和拒绝策略,若热任务量下降了(多余空闲线程的存活时间)会缩容
<6>线程池的拒绝策略
AbortRolicy(默认):直接抛出异常,阻止运行
CallerRunsPoclicy:调用者运行机制
DiscardOldestPolicy:抛弃队列中等待最久的任务
DiscardPolicy:直接丢弃任务,不处理也不抛异常
<7>合理配置线程池如何考虑?
CPU密集型:CPU核数+一个线程
IO密集型:
①任务线程并不是一直在执行任务,则应配置尽可能多的线程,如CPU核数*2
②大部分线程都阻塞,需要多配置线程数,参考公式:CPU核数/(1-阻塞系数) --阻塞系数在0.8~0.9之间
9、死锁编码及定位分析
<1>产生死锁的原因:系统资源不足,进程运行推进的顺序不合适,资源分配不当
<2>解决:jps定位进程,jstack找到死锁查看