Java面试笔记
Java面试笔记-网络模块
TCP的三次握手
TCP报文头解读:
源端口(2字节)和目的端口(2字节):
序列号Sequence Number (Seq)(4字节):
Acknowledgment Number ACK确认码 (4字节):
Offset 偏移量 (4bits,即半个字节):
Reserved 保留域 (4bits):
TCP Flags TCP控制位 (1字节):
Window 滑动窗口 (2字节):
Checksum 奇偶校验 (2字节):
Urgent Pointer 紧急指针 (2字节):
TCP Options TCP可选项:
TCP的三次握手流程详解:
TCP三次握手的相关问题:
TCP的四次挥手
TCP四次挥手相关的问题:
全双工:双向同步通信。
一次tcp连接。从左到右分别为:第几次抓报的序号,抓包的时间,源IP地址,目的IP地址,协议,长度,信息。
其中信息的第一个53627 -> 80 为两边的端口号。
注:ACK确认报文不消耗Seq序列号,第四次和第六次包含了HTTP协议的TCP通信仍然序列号为1。
UDP和TCP的区别
UDP的报文结构:
Source Port: 源端口
Destination Port: 目标端口
Length: 数据包长度
Checksum: 奇偶校验值
data: 用户数据。
UDP的特点:
结论: UDP和TCP的区别
TCP的滑动窗口详解
TCP使用滑动窗口做流量控制与乱序重排
保证TCP的可靠性
保证TCP的流控特性
TCP的window字段:
用以告诉发送端接收端缓存的大小,以此控制发送端发送数据的速率。
TCP滑动窗口的计算过程:
滑动窗口的滑动原理:
发送方:
滑动窗口由category2和category3组成。
接收方:
滑动窗口由3组成。
HTTP简介
超文本传输协议HTTP主要特点:
支持客户/服务器模式:客户端通过url想服务端发送请求信息,服务端发送相应信息给客户端。
简单快速: 发送请求方法的时候只需发送请求的方法和路径, 请求的方法有get/post。程序规模小,传输速度快。
灵活:传输的数据类型多,使用content-type进行标记。
无连接: 每次连接只处理一个请求,服务器处理完客户的请求并收到客户的应答之后就断开连接,节省传输时间。http 1.1 版本之后默认使用长连接,在客户端应答之后等待一段时间才断开连接。
关于http的长连接理解:HTTP的长连接详解
无状态:协议对于事务处理没有记忆能力,如果处理的事务需要前面的信息则必须要重传。
HTTP请求结构:
HTTP 响应结构:
HTTP 请求/响应的步骤:
在浏览器窗口输入URL之后,按下回车后的经历:
HTTP 状态码:
GET请求和POST请求的区别
Cookie和Session的区别
Cookie的设置以及发送过程:
HTTP和HTTPS的区别
SSL(Security Sockets Layer,安全套接层)简介:
HTTPS数据传输流程:
在TCP连接建立之后:
注:所以原来所需要的3次连接之后发送消息,变成了3次连接+4次加密之后发送消息。
详细版本:
HTTP和HTTPS的区别:
Socket简介
Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口
Scoket通信流程:
Java面试笔记-数据库模块
如何设计一个关系型数据库:
存储:就像os文件系统,将数据最终持久化存入磁盘中
存储管理:把逻辑存储最终映射到物理存储中,并且实现性能高效,及尽量少的做io,以为一次读一行的io和一次读多行的io消耗的性能基本差不多,所以每次读取尽量读多行或者块。
缓存机制:多读出来的数据放入缓存中,方便下次快速查找。
索引简介
时间复杂度快速分析
数据结构基础
相关考点:
Java集合框架简介
集合的源码大都集中在java.util这个包下面。
注:HashSet是由HashMap实现的。
TreeSet底层是由NavigableMap实现的,NavigableMap是由TreeMap实现的.
Map详解:
Map的key是通过Set实现的,在Map中的keySet()方法返回一个Set集合,所以可以去重.
Map的value是通过Collection实现的,所以允许重复。
HashMap(Java8 以前):数组+链表,数组查询快,删改慢;链表删改快,查询慢.,并且HashMap是线程不安全的,所以效率比较高。
缺点:如果连续发生哈希碰撞,则会使一个链表不停的增长,从而使性能从O(1)变为O(n)。
HashMap(Java8 及以后):数组+链表+红黑树。
连续发生哈希碰撞的情况性能优化为:性能从O(n)提高到O(logn)。
HashMap:从获取hash到散列的过程
HashMap、HashTable、 ConccurentHashMap的区别:
HashMap是采用的lazy load在首次使用的时候才会初始化:
HashMap:put方法的逻辑:
TREEIFY_THRESHOLD=8,MIN TREEIFY CAPACITY=64
超过8开始调用resize扩容,超过64进行树化。
HashMap:如何有效减少碰撞
HashMap扩容的问题:
HashTable 简介:
ConcurrentHashMap: 简介
CAS+synchronized使锁更细化
在ConcurrentHashMap中,synchronized只锁定当前链表或者红黑树的首节点,因此只要不发生哈希冲突就不会造成线程堵塞。
ConcurrentHashMap不允许键或值为空,HashMap可以。
ConcurrentHashMap:put方法的逻辑
ConcurrentHashMap总结:
HashMap、HashTable、 ConccurentHashMap的区别总结:
二叉树
一般考点为二叉搜索树,即左边的子节点要小于根节点,右边的子节点要大于根节点。
二叉树实现(python):
此为二叉搜索树
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def insert(root, key):
if root is None:
return Node(key)
else:
if root.val < key:
root.right = insert(root.right, key)
else:
root.left = insert(root.left, key)
return root
root = None
keys = [50, 30, 20, 40, 70, 60, 80]
for key in keys:
root = insert(root, key)
二叉平衡树(AVL):
一个空树或者一个树的两个子节点的高度差不超过1.
不满足二叉平衡树的时候会进行左旋或者右旋。
左旋
本质就是,创建一个新的节点作为根节点的左子树,然后抛弃掉原本的右子树,让原本右子树的右子树成为新的右子树。
图解左旋:
右旋思路同理。
B 树
B树的特征:
B+树:
B+树的特征:
B+树为什么更适合做储存索引:
Hash索引:
特点:
缺点:
BitMap位图索引简介:
特点:
缺点:
数据库索引部分总结:主流的索引是B+树,哈希索引和位图索引较小众。
索引模块:
密集索引和稀疏索引的区别:
MySql的主要存储引擎:
MyISAM和InnoDB的区别:
索引模块考点:
如何优化慢查询Sql:
分三步,
进入到数据库可视化界面中:
1. 根据慢日志定位慢查询sql
## 查询和查询相关的变量
show variables like '%quer%';
## 在显示的变量中, slow_query_log代表是否开启慢日志,
## long_query_time代表设置执行时间超过多少秒的sql记录到慢日志中。
## slow_query_log_file表示慢日志文件的位置。
# 显示慢日志的数量
show status like '%slow queries%';
# 打开慢日志并设置记录时间为1秒钟。
# 注:在Query界面中用命令设置的参数在数据库重启后就会恢复默认设置,想要永久改变参数就要在ini文件中配置参数。
set global slow_query_log = on
set global long_query_time = 1
# 执行查询语句
select count(id) from person_info_large;
select name from person_info_large order by name desc;
2.使用explain等工具分析sql
# 在sql语句前加上explain即可分析sql查询语句情况。
explain select name from person info large order by name desc;
# explain返回的参数,id表示sql执行的顺序,id越大的越先执行。
# type:表示mysql找到数据行需要的方式,其中index和all为全表扫描。
# system>const>eq_ref>ref>fulltext>ref_or_null>index merge>unique_subquery>index subquery>range>index>all
# extra:需要优化的参数见下图
3.修改sql或者尽量让sql走索引
# 修改sql,查找有没有合适的索引,让查询参数查询带索引的关键字。
#原sql使用name字段,不是索引关键字。
select name from person_info_large order by name desc;
#查找建表语句后发现account字段有索引,修改后的sql如下。
select account from person info_large order by account desc;
# 为查询的字段添加索引。
alter table person_info_large add index idx_name(name);
#通过强制使用某个索引,测试使用不同索引的查询时间
explain select info larae force index (primary);
最左匹配原则
索引是建立得越多越好吗
锁简介
MyISAM与InnoDB关于锁方面的区别是什么
MyISAM适合的场景:
InnoDB适合的场景:
数据库锁的分类:
#1.先读取test_innodb的数据,得到Version的值为versionValue
select version from test innodb where id =2;#0
#2每次更新test_innodb表中的money字段时候,为了防止发生冲突,先去检查version再做更新#更新成功的话version +1
update test innodb set money = 123, version = 0 + 1 where version = 0 and id = 2;
#3假设在执行更新语句前,先执行了下面的更新操作。
update test innodb set money = 345, version = 0 + 1 where version = 0 and id = 2;
#那么在执行第一个更新语句就会更新失败,即返回的更新行数为0. 这时候可以根据程序中返回的count值对前端传输错误信息,提示数据更新失败。
update test innodb set money = 123, version = 0 + 1 where version = 0 and id = 2;
#这种手动提交时对比版本号的锁就是实现乐观锁的一种。
数据库事物的四大特性
ACID
事务隔离级别以及各级别下的并发访问问题:
当前读和快照读:
RC、RR级别下的InnoDB的非阻塞读如何实现:
InnoDB可重复读隔离级别下如何避免幻读:
Redis相关知识
主流应用架构:
缓存中间件:Memcache和Redis的区别
为什么Redis能这么快:
多路I/O复用模型:
传统的阻塞I/O模型:
多路I/O复用模型:
Redis采用的I/O多路复用函数:epoll/kqueue/evport/select ?
Redis的数据类型:
底层数据类型基础(非重点):
面试考点:从海量Key里查询出某一固定前缀的Key
如何通过Redis实现分布式锁
分布式锁需要解决的问题:
实现锁的步骤:
RedisService redisService = SpringUtils.getBean(RedisService class);
String result = redisService.set(lockkey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if ("OK".equals(result)) (
//执行独占资源逻辑
doOcuppiedWork()
}
如何使用redis实现异步队列:
Redis如何做持久化:
Pipeline的优点:
相当于用一个buffer先存储一些从客户端发送的请求,然后批量发送给服务端进行处理,然后在批量发回给客户端,以此减少服务端和客户端之间的通信次数,提升处理性能。
Redis的同步机制:
哨兵机制 Redis Sentinel:
Redis的集群原理:
Java 虚拟机 JVM
面试考题:
谈谈你对Java的理解
设计的知识点:
平台无关性
GC
语言特性
面向对象
类库异常处理
平台无关性
Compile Once, Run Anywhere如何实现:
在配置了环境变量后,在执行javac指令的时候系统会直接去指定的jdk/bin目录下找到javac程序并执行。
class文件保存的就是java文件编译后的字节码, 之后使用java指令就能执行class文件并打印输出。
javap: jdk自带的反汇编器,可以查看java编译器生成的字节码。
javap -c: 反汇编,把字节码文件转成能看懂的java文件。
为什么JVM不直接将源码解析成机器码去执行
准备工作:每次执行都需要各种检查
兼容性:也可以将别的语言解析成字节码
JVM如何加载.class文件:
JVM是内存中的虚拟机:
谈谈反射:
反射的例子:
public class Robot {
private String name;
public void sayHello(String helloSentence){
System.out.println(helloSentence+name);
}
private String sayHello2(String tag){
return "hello" + tag;
}
}
public class Reflect {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 获取类
Class rc = Class.forName("com.example.reflactexample1.demos.web.Robot");
// 创建类的实例,并强转,因为Class.forName返回的是T泛型。
Robot r = (Robot) rc.newInstance();
System.out.println("Class Name is:" + rc.getName());
// 获取方法对象,getDeclaredMethod能够获取private、 public、 protected的方法,或者所实现的接口的方法,不能获取继承的方法。
Method getHello2 = rc.getDeclaredMethod("sayHello2", String.class);
//访问私有方法时,要设置accessible为true,默认为false,如果不设置就会报错。
getHello2.setAccessible(true);
//调用方法,需要传入类实例和类的方法参数,invoke默认返回Object。
Object str = getHello2.invoke(r, "user001");
System.out.println("私有方法getHello2的输出为:" + str);
// getMethod方法只能获取到类的public方法或者继承的方法和所实现的接口的方法。
Method getHello1 = rc.getMethod("sayHello", String.class);
getHello1.invoke(r, "Hello aaa");
// 获取类的私有属性
Field name = rc.getDeclaredField("name");
//设置accessible为true,因为是私有属性
name.setAccessible(true);
name.set(r, "XiaoMing");
getHello1.invoke(r, "Hello aaa");
}
}
反射的目的:
反射的作用:
谈谈ClassLoader:类从编译到执行的过程
ClassLoader定义:
ClassLoader的种类:
ClassLoader源码解析:
通过classLoader类的源码可以看出,负责加载类的loadClass方法,通过传入类的名字,返回代表这个类的Class类实例。
//源码第521行。
// 具体loadClass的实现,在之后的段落中。
// 关于参数resolve,也就是loadClass的第二个参数。如果之前使用过这个类,则设置为true,否则设置为false。If the class was found using the above steps, and the resolve flag is true, this method will theninvoke the resolveclass(Class) method on the resulting class object.
/**
* Loads the class with the specified <a href="#binary-name">binary name</a>.
* This method searches for classes in the same manner as the {@link
* #loadClass(String, boolean)} method. It is invoked by the Java virtual
* machine to resolve class references. Invoking this method is equivalent
* to invoking {@link #loadClass(String, boolean) loadClass(name,
* false)}.
*
* @param name
* The <a href="#binary-name">binary name</a> of the class
*
* @return The resulting {@code Class} object
*
* @throws ClassNotFoundException
* If the class was not found
*/
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
//Params:
//name – The binary name of the class
//resolve – If true then resolve the class
//Returns:
//The resulting Class object
注意:ClassLoader不同版本的变化:
jdk 11版本源码(ClassLoader.java):
// the built-in class loaders
private static final BootClassLoader BOOT_LOADER;
private static final PlatformClassLoader PLATFORM_LOADER;
private static final AppClassLoader APP_LOADER;
每次用到class时,classLoader都会去这些路径下面查看是否有对应的文件。如果有文件就加载进系统。
类加载器的双亲委派机制:
注:如果底层的classLoader已经加载过类,则直接返回之前加载的类,如果没有加载过这个类,则首先把这个加载向上抛出,一直抛到最顶层的BootStrap ClassLoader,所以对于没加载过的类,最顶层的classLoader会首先开始尝试加载,如果无法加载再向下交给下层的classLoader完成。
使用双亲委派机制去加载类的目的:
类的加载方式:
loadClass和forName的区别:
类的装载过程。
Java 内存模型:
内存简介:
地址空间的划分:
JVM内存模型 – JDK8版本:
程序计数器(Program Counter Register):
Java虚拟机栈(Stack):
局部变量表和操作数栈:
递归为什么会引发java.lang.StackOverflowError异常:
虚拟机栈过多会引发java.lang.OutOfMemoryError异常:
本地方法栈
元空间(MetaSpace)与永久代(PermGen)的区别
MetaSpace相比PermGen的优势:
Java堆(Heap):
JVM调优:
JVM 三大性能调优参数-Xms -Xmx-Xss的含义:
Java内存模型中堆和栈的区别一内存分配策略:
一些关于内存的补充:
不同JDK版本之间的intern()方法的区别-JDK6 VS JDK6+:
Java 垃圾回收机制
判断对象为垃圾的标准:
几种垃圾回收算法:
复制算法(Copying):
标记-整理算法(Compacting):
主流算法 - 分代收集算法(Generational Collector):
jdk6 , jdk7:
Jdk8及其以后的版本:
GC的分类:
年轻代:尽可能快速地收集掉那些生命周期短的对象
对象如何晋升到老年代:
常用的调优参数:
老年代:存放生命周期较长的对象:
老年代:
触发FuIl GC的条件:
Stop-the-World:
Safepoint:
JVM的运行模式:
常见的垃圾收集器:
年轻代常见的垃圾收集器:
Serial收集器(-XX:+UseSerialGc,复制算法):
ParNew收集器(-XX:+UseParNewGC,复制算法):
Parallel Scavenge收集器(-XX:+UseParallelGC,复制算法):
老年代常见的垃圾收集器:
Serial Old收集器(-XX:+UseSerialOldGC,标记-整理算法):
Parallel Old收集器(-XX:+UseParallelOldGC,标记-整理算法):
CMS收集器(-XX:+UseConcMarkSweepGC,标记-清除算法):
G1收集器(-XX:+UseG1GC, 复制+标记-整理算法):
Garbage First收集器的特点:
GC相关的面试题:
Object的finalize()方法的作用是否与C++的析构函数作用相同:
Java中的强引用,软引用,弱引用,虚引用有什么用:
强引用(Strong Reference ):
软引用(Soft Reference):
弱引用(Weak Reference):
虚引用(PhantomReference):
强引用 >软引用 >弱引用 >虚引用
类层次结构:
引用队列(ReferenceQueue):
// ReferenceQueue源码第51行,可以看出队列只保存了一个head变量,剩下的链表通过enqueue方法的next储存。
static private class Lock{};
private Lock lock = new Lock();
private volatile Reference<? extends T> head = null;
private long queueLength=0;
//注意enqueue方法是传入一个reference对象
boolean enqueue(Reference<?extends T>r)
synchronized(lock){
ReferenceQueue<?>queue =r.queue;
if((queue ==NULL) || (queue == ENQUEUED)){
return false;
}
assert queue == this;
r.queue = ENQUEUED;
r.next=(head== null)?r:head; //传入的Reference对象的next指向head,也就是当前引用队列保存的值。
head = r;
queueLength++;
// Reference类的源码。类中有next属性值。
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T>queue;
/* When active:NULL
* pending:this
* Enqueued:next reference in queue (or this if last)
* Inactive:this
*/
/rawtype5/
Reference next;
多线程和并发
进程和线程的由来:
Java进程和线程的关系:
注:一个程序是一个可执行的文件,而一个进程则是一个执行中程序的实例。
JVM虚拟机并非单线程,虽然只创建一个主线程用于执行任务,但是也会创建其他的线程如GC垃圾回收线程。
Thread中的start和run方法的区别:
private static void attack(){
System.out.println("Fight")System.out.println("current Thread is :"+ Thread.currentThread().getName());
}
public static void main(String[]args){
Thread t=new Thread(){
public void run(){
attack();
}
};
System.out.println("current main thread is :" + Thread.currentThread().getName());
//t.run();
t.start();
Thread和Runnable是什么关系:
// Thread类和Runnable接口的源码
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
public interface Runnable {
abstract void run();
}
如何给run()方法传参:
实现的方式主要有三种:
构造函数传参
成员变量传参
回调函数传参
如何实现处理线程的返回值:
实现的方式主要有三种:
// 如果子线程还未处理完成,主线程直接继续执行,那么就无法得到子线程的返回值,所以可以使主线程循环等待,查询子线程执行完成,继续执行主线程的程序。
public class CycleWait implements Runnable{
private String value;
public void run(){
try {
Thread.currentThread().sleep(millis:5000);
}catch(InterruptedException e){
e.printStackTrace();
}
value ="we have data now";
}
// 使用主线程等待法之前的main函数,输出Null。
public static void main(String[] args){
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
t.start();
System.out.println("value : " + cw.value);
}
//使用主线程等待法之后的main函数,输出:we have data now
public static void main(String[] args){
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
t.start();
while(cw.value == null){
Thread.currentThread().sleep( millis:100);
}
System.out.println("value : " + cw.value);
}
//使用t.join法之后的的main函数,输出:we have data now
public static void main(String[] args){
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
t.start();
t.join();
System.out.println("value : " + cw.value);
}
//Callable接口源码代码。
public interface Callable<V>{
V call()throws Exception;
}
// FutureTask.java的源码第124行。可以看出FutureTask类的构造方法能够接收Callable类。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
// FutureTask.java的源码第160行,能够判断Callable实力里面的call方法是否执行完成。
public boolean isDone() {
return state != NEW;
}
// FutureTask.java的源码第254行,FutureTask<V> implements RunnableFuture<V> {}实现了RunnableFuture,
// RunnableFuture<V> extends Runnable, Future<V>{} RunnableFuture接口实现了Runnable接口,所以
// FutureTask中重写了Runable的run方法。
// 而FutureTask中的run方法又执行了callable中的call方法。
// 所以,如果在创建FutureTask实例的时候传入callable实例,从而执行带参构造函数,就可以通过FutureTask中的run方法执行实现了callable接口的实例的call方法。
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// FutureTask.java的源码第185行,如果子线程没完成进行等待,完成了就返回值。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
// 带时间控制的获得返回值方法。
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
代码例子:callable和futureTask。
public class MyCallable implements Callable<String>{
@Override
public string call()throws Exception{
String value="test";
System.out.println("Ready to work");
Thread.currentThread().sleep( millis:5000);
System.out.println("task done");
return value;
}
}
public class FutureTaskDemo{
public static void main(String[] args)throws ExecutionException, InterruptedException{
FutureTask<String> task= new FutureTask<String>(new MyCallable());
new Thread(task).start();
if(!task.isDone()){
System.out.println("task has not finished, please wait!");
}
System.out.println("task return:"+ task.get());
}
}
线程池的例子。
public cassThreadPoolDemo{
public static void main(String[]args){
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
Future<String> future = newCachedThreadPool.submit(new MyCallable());
if(!future.isDone()){
System.out.println("task has not finished, please wait!");
}
try {
System.out.println(future.get());
} catch (InterruptedException e){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
} finally {
newCachedThreadPool.shutdown();
}
}
}
线程的状态:
六个状态:
sleep和wait的区别:
notify和notifyAl的区别:
yield:
如何中断线程:
线程转换图:
synchronized
线程安全问题的主要诱因:
互斥锁的特性
synchronized 锁的不是代码,锁的都是对象。
根据获取的锁的分类:获取对象锁和获取类锁:
对象锁和类锁的总结:
实现synchronized的基础:
Java对象头
Monitor
对象在内存中的布局:
对象头
实例数据
对齐填充
对象头的结构:
MarkWord:
Monitor:每个Java对象天生自带了一把看不见的锁。本身也是一个对象。
对象头的重量级锁(也就是synchronized):记录的是指向Monitor的起始地址的指针。每个对象都有一个Monitor与之关联。
Monitor锁的竞争、获取与释放:
什么是重入:
为什么会对 synchronized 嗤之以鼻:
Java6以后,synchronized性能得到了很大的提升:
自旋锁与自适应自旋锁:
synchronized的四种状态:
synchronized和ReentrantLock的区别:
ReentrantLock(再入锁):
位于java.util.concurrent.locks包
和CountDownLatch、FutureTask、Semaphore-样基于AQS实现
能够实现比synchronized更细粒度的控制,如控制fairness
调用lock()之后,必须调用unlock()释放锁
性能未必比synchronized高,并且也是可重入的
ReentrantLock公平性的设置:
ReentrantLock将锁对象化:
是否能将wait\notify\notifyAl对象化:
总结:
什么是Java内存模型中的happens-before:
Java内存模型JMM:
JMM中的主内存和工作内存:
JMM如何解决可见性问题:
指令重排序需要满足的条件:
A操作的结果需要对B操作可见,则A与B存在happens-before关系:
private int value = 0;
//线程1执行写操作
public void write(int input ){
value = input;
}
//线程2执行读操作
public int read(){
return value;
}
CAS( Compare and Swap ):
Java线程池:
为什么要使用线程池:
ThreadPoolExecutor:
handler:线程池的饱和策略:
新任务提交execute执行后的判断:
线程池的状态:
状态转换图:
工作线程的生命周期:
String ,StringBuffer ,StringBuilder的区别:
Java 异常机制知识:
Error和Exception的区别:
Java的异常体系:
从概念角度解析Java的异常处理机制:
常见Error以及Exception:
Java的异常处理机制:
Java异常的处理原则:
高效主流的异常处理框架:
在用户看来,应用系统发生的所有异常都是应用系统内部的异常:
Java异常处理消耗性能的地方:
BlockingQueue:提供可阻塞的入队和出队操作:
//尝试往队列尾部去添加元素,添加成功返回true,添加失败抛出异常信息。
boolean add(@NotNull E e);
//尝试往队尾添加元素,成功返回true,失败返回false。
boolean offer( @NotNull E e);
//尝试往队列尾部去添加元素,如果队列满了则阻塞当前线程一直到添加成功。
void put( @NotNull E e)throws InterruptedException;
//尝试往队列尾部添加元素,如果队列满了,则等待timeout时间,如果时间到了还没添加成功,则返回false。
boolean offer(E e,long timeout, @NotNull TimeUnit unit)
throws InterruptedException;
// 从队列头部取元素,如果队列为空,则一直尝试取元素直到成功为止。
@NotNull E take()throws InterruptedException;
//尝试从队列头部获取元素,如果队列为空则等待timeout时间,如果时间到了还没取到头部元素,则返回空。
@Nullable E poll(long timeout, @NotNull TimeUnit unit)
throws InterruptedException;
//获取当前队列可获取元素数量。
int remainingCapacity();
//从队列中删除指定的对象。
boolean remove(Object o);
//查看队列中是否包含指定的对象
boolean contains(Object o);
// 把对象转移到指定的集合中。
int drainTo( @NotNull Collection<? super E> c);
Java的IO机制:
BIO、NIO、AIO的区别:
IOC(Inversion of Control):控制反转
ApplicationContext的功能(继承多个接口):
Bean的生命周期:
Spring AOP知识:
ACID
隔离级别
事务传播