0%

.image-20220317230058398

错误的作用域会出现并发问题

  • SqlSessionFactoryBuilder

    建造者模式,一旦用此创建了SqlSessionFactory,便不再需要它了

    因此它应该作为局部变量

  • SqlSessionFactory

    类似于数据库连接池,一旦被创建就应该在程序运行期间一直存在,并且应该只有一个实例

    应该使用单例模式

  • SqlSession

    SqlSession实例不是线程安全的,因此不能被共享

    应该在每个方法中创建一个实例,方法结束后就close()

Java爬取https网址抛出SunCertPathBuilderException: unable to find valid certification path to requested target

是因为 $env:JAVA_HOME/lib/security/cacerts 中没有对应的CA,需要手动将SSL证书加入

亲测下列解决步骤适用于Win10和Win11

下载证书

以Chrome为例

阅读全文 »

为什么索引用B+树?

.image-20220405102005556

  • B树
  • 红黑树
  • AVL树

聚簇索引 & 非聚簇索引

聚簇:数据和索引在一起

阅读全文 »

事务的ACID属性

  • Atomicity原子性
  • Consistency一致性:从实际的业务逻辑上来说,最终结果是对的,是跟期望的结果完全符合的,比如转账的加减
  • Isolation隔离性
  • Durability持久性:事务一旦提交,数据会持久化到硬盘,他对数据库的改变是永久性的

四大隔离级别★

脏读 Dirty read 不可重复读 Non repeatable read 幻读 Phantom read
读未提交 Read uncommited ×
读已提交 Read commited × ×
可重复读 Repeatable read × ×
穿行 Serializable × × ×

MVCC

阅读全文 »

快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static void quickSort(int[] nums){
sort(nums,0,nums.length-1);
}

private static void sort(int[] nums, int l,int r){
if(l>=r){return;}
int m = partition(nums, l, r);
sort(nums,l,m-1);
sort(nums,m+1,r);
}
private static int partition(int[] nums,int left,int right){
swap(nums,(int)(Math.random()*(right-left+1))+left,left); //important
int l=left+1,r=right;
while(true){
while(l<=right && nums[l]<=nums[left]){
l++;
}
while(r>left && nums[r]>=nums[left]){
r--;
}
if(l>=r){break;}
swap(nums,l,r);
}
swap(nums,left,r);
return r;
}
private static void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}

LRU

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@SuppressWarnings("FieldMayBeFinal")
public class LRUCache<K,V>{
private Node<K,V> head=new Node<>();
private Node<K,V> tail=new Node<>();
private Map<K,Node<K,V>> map=new HashMap<>();
private int remain; //剩余容量

public LRUCache(int capacity){
remain=capacity;
head.next=tail;
tail.pre=head;
}

public V get(K key){
Node<K,V> node = map.get(key);
if(node==null){return null;}
//在链表中删除
node.pre.next=node.next;
node.next.pre=node.pre;
//插入头部
node.next=head.next;
node.pre=head;
head.next.pre=node;
head.next=node;
return node.value;
}

//返回key对应的value
public V remove(K key){
Node<K,V> node = map.remove(key);
if(node==null){return null;}
//在链表中删除
node.pre.next=node.next;
node.next.pre=node.pre;
remain++;
return node.value;
}

//返回旧value
public V put(K key, V value){
if(get(key)!=null){ //顺便更新key的位置
Node<K,V> node = map.get(key);
V oldVal=node.value;
node.value=value;
return oldVal;
}else if(remain==0){ //容量已满,删除尾部节点
remove(tail.pre.key); //就是因为这一步,才需要在Node中存key
}
//在链表头部插入节点
Node<K,V> node = new Node<>(key, value, head, head.next);
head.next.pre=node;
head.next=node;
map.put(key,node);
remain--;
return null;
}

private static class Node<K,V>{
private K key;
private V value;
private Node<K,V> pre,next;

private Node(){}

private Node(K key, V value, Node<K,V> pre, Node<K,V> next){
this.key=key;
this.value=value;
this.pre=pre;
this.next=next;
}
}
}

阅读全文 »

类加载过程

  1. 加载】JVM把.class文件加载到方法区,生成构造函数、接口、静态变量(static)、常量(final)池、静态代码块

    并在堆中生成对应的Class对象

  2. 链接】将类的二进制代码合并到JVM的运行状态之中
    • 验证
    确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备
    正式为static变量分配内存并设置默认值(和Bean实例化很像),具体赋值在初始化阶段完成
    • 解析
    虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程

  3. 初始化】如果发现其父类还没有进行过初始化,则需要先初始化其父类

    ​ 顺序如下(其实很有规律)

    1、父类的静态变量
    2、父类的静态代码块
    3、子类的静态变量
    4、子类的静态代码块
    5、父类的非静态变量
    6、父类的非静态代码块
    7、父类的构造方法
    8、子类的非静态变量
    9、子类的非静态代码块
    10、子类的构造方法

.image-20220220153623885

双亲委派机制

自定义ClassLoader→AppClassLoader(自定义的类)→PlatformClassLoader(jre/lib/ext/*.jar)→BootClassLoader(jre/lib/rt.jar)

阅读全文 »

.image-20220220152851185

.image-20220308144610966

PC计数器、本地方法栈、栈为线程私有,堆、元空间为线程共享

关于常量池:

  • JDK1.6及之前:在方法区中
  • JDK1.7:在永久区中
  • JDK1.8:在元空间中(JDK1.8 永久区+方法区→元空间)
阅读全文 »

线程池的优点:

  1. 线程复用节省开销,从而加快响应速度

  2. 方便管理(如控制最大并发数)

核心:三大方法,七大参数,四种拒绝策略

三大方法

1
2
3
4
5
6
7
8
9
ExecutorService threadPool=

Executors.newSingleThreadExecutor();//单线程的线程池
Executors.newFixedThreadPool(5);//固定(Fixed)线程的线程池
Executors.newCachedThreadPool();//缓存线程池(可以自己调整)

threadPool.execute(()->{
//具体执行代码
});
阅读全文 »

.image-20220420121146774

ReentrantLock

  • ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”
  • ReentrantLock在同一个时间点只能被一个线程锁持有,“可重入”意为ReentrantLock锁可以被同一个线程多次获取
  • 公平锁与非公平锁
    • 公平锁:就是谁等待时间最长,谁就先获取锁,ReentrantLock中用AQS队列实现
    • 非公平锁:随机获取,CPU时间片轮询到哪个线程,哪个线程就能获取锁

使用语法

1
2
3
4
5
6
Lock lock=new ReentrantLock();
try{
lock.lock();
}finally{
lock.unlock();
}
阅读全文 »