Skip to content

Commit ede635a

Browse files
committed
update note
1 parent 9e63151 commit ede635a

9 files changed

Lines changed: 253 additions & 40 deletions

File tree

Java/Java web.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ Servlet是什么?
2424

2525
## jsp
2626

27-
Java server pages。当有人请求JSP时,服务器内部会经历一次动态资源(JSP)到静态资源(HTML)的转化,服务器会自动帮我们把JSP中的HTML片段和数据拼接成静态资源响应给浏览器。也就是说JSP运行在服务器端,但最终发给客户端的都已经是转换好的HTML静态页面(在响应体里)。服务器并没有把JSP文件发给浏览器
27+
Java server pages。当有人请求JSP时,服务器会自动帮我们把JSP中的HTML片段和java代码拼接成静态资源响应给浏览器。也就是说JSP运行在服务器端,但最终发给客户端的都已经是转换好的HTML静态页面(在响应体里)。
2828

2929
即:**JSP = HTML + Java片段**(各种标签本质上还是Java片段)

Java/Java8.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
33

44

5+
- [函数式编程](#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B)
56
- [Lambda 表达式](#lambda-%E8%A1%A8%E8%BE%BE%E5%BC%8F)
67
- [函数式接口](#%E5%87%BD%E6%95%B0%E5%BC%8F%E6%8E%A5%E5%8F%A3)
78
- [内置的函数式接口](#%E5%86%85%E7%BD%AE%E7%9A%84%E5%87%BD%E6%95%B0%E5%BC%8F%E6%8E%A5%E5%8F%A3)
@@ -16,10 +17,28 @@
1617
- [Reduce](#reduce)
1718
- [Parallel-Streams 并行流](#parallel-streams-%E5%B9%B6%E8%A1%8C%E6%B5%81)
1819
- [Map 集合](#map-%E9%9B%86%E5%90%88)
19-
- [函数式编程](#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B)
20+
- [函数式编程](#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B-1)
2021

2122
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2223

24+
## 函数式编程
25+
26+
面向对象编程:面向对象的语言,一切皆对象,如果想要调用一个函数,函数必须属于一个类或对象,然后在使用类或对象进行调用。面向对象编程会多写很多可能是重复的代码行。
27+
28+
```java
29+
Runnable runnable = new Runnable() {
30+
@Override
31+
public void run() {
32+
System.out.println("do something...");
33+
}
34+
};
35+
36+
```
37+
38+
函数式编程:在某些编程语言中,如js、c++,我们可以直接写一个函数,然后在需要的时候进行调用,即函数式编程。
39+
40+
41+
2342
## Lambda 表达式
2443

2544
[java8新特性](https://juejin.im/post/5c3d7c8a51882525dd591ac7#heading-16)
@@ -35,7 +54,7 @@ names.sort((a, b) -> b.compareTo(a));
3554

3655
## 函数式接口
3756

38-
函数式接口(Functional Interface)就是只包含一个抽象方法的声明。只有那些函数式接口才能缩写成 Lambda 表示式
57+
函数式接口(Functional Interface)就是只包含一个抽象方法的接口。只有函数式接口才能缩写成 Lambda 表达式
3958

4059
```java
4160
@FunctionalInterface //定义为一个函数式接口,如果添加了第二个抽象方法,编译器会立刻抛出错误提示。

Java/Java基础.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
- [ConcurrentHashMap 和 Hashtable 的key和value不能为null?](#concurrenthashmap-%E5%92%8C-hashtable-%E7%9A%84key%E5%92%8Cvalue%E4%B8%8D%E8%83%BD%E4%B8%BAnull)
2424
- [treemap底层](#treemap%E5%BA%95%E5%B1%82)
2525
- [list/hashset/hashmap排序](#listhashsethashmap%E6%8E%92%E5%BA%8F)
26+
- [Stack](#stack)
27+
- [ArrayDeque](#arraydeque)
2628
- [ThreadLocal](#threadlocal)
2729
- [StringBuilder和StringBuffer](#stringbuilder%E5%92%8Cstringbuffer)
2830
- [线程安全类](#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%B1%BB)
@@ -261,6 +263,26 @@ ConcurrentHashmap和Hashtable都支持并发,当你通过get(k)获取对应的
261263

262264
https://blog.csdn.net/whuxiaoqiang/article/details/9371923
263265

266+
### Stack
267+
268+
Stack继承自 Vector 类,peek()、pop()、push()和 search()都是同步方法。Vector底层用数组实现(默认长度为10)。效率较低,不推荐使用。
269+
270+
### ArrayDeque
271+
272+
ArrayDeque实现了双端队列,内部使用循环数组实现,默认大小为16。
273+
274+
特点:
275+
276+
1. 在两端添加、删除元素的效率较高
277+
278+
2. 根据元素内容查找和删除的效率比较低。
279+
280+
3. 没有索引位置的概念,不能根据索引位置进行操作。
281+
282+
ArrayDeque和LinkedList都实现了Deque接口,如果只需要从两端进行操作,ArrayDeque效率更高一些。如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除(LinkedList有相应的 api,如add(int index, E e)),则应该选LinkedList。
283+
284+
ArrayDeque和LinkedList都是线程不安全的,可以使用Collections工具类中synchronizedXxx()转换成线程同步。
285+
264286

265287

266288
## ThreadLocal

Java/并发.md

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- [线程池](#%E7%BA%BF%E7%A8%8B%E6%B1%A0)
66
- [线程池原理](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86)
77
- [线程池大小](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%A4%A7%E5%B0%8F)
8+
- [关闭线程池](#%E5%85%B3%E9%97%AD%E7%BA%BF%E7%A8%8B%E6%B1%A0)
89
- [executor框架](#executor%E6%A1%86%E6%9E%B6)
910
- [简介](#%E7%AE%80%E4%BB%8B)
1011
- [ThreadPoolExecutor Demo](#threadpoolexecutor-demo)
@@ -49,9 +50,9 @@
4950
- [并发的条件](#%E5%B9%B6%E5%8F%91%E7%9A%84%E6%9D%A1%E4%BB%B6)
5051
- [进程线程](#%E8%BF%9B%E7%A8%8B%E7%BA%BF%E7%A8%8B)
5152
- [线程状态](#%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81)
53+
- [中断](#%E4%B8%AD%E6%96%AD)
5254
- [join/yeild/sleep/wait/park](#joinyeildsleepwaitpark)
5355
- [wait()和sleep()的区别](#wait%E5%92%8Csleep%E7%9A%84%E5%8C%BA%E5%88%AB)
54-
- [中断](#%E4%B8%AD%E6%96%AD)
5556
- [原子类](#%E5%8E%9F%E5%AD%90%E7%B1%BB)
5657
- [基本类型原子类](#%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
5758
- [数组类型原子类](#%E6%95%B0%E7%BB%84%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
@@ -141,6 +142,25 @@ CPU 密集型任务(N+1): 这种任务消耗的主要是 CPU 资源,可以
141142

142143
I/O 密集型任务(2N): 系统会用大部分的时间来处理 I/O 交互,而线程等待 I/O 操作会被阻塞,释放 cpu,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法:最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU核心数 * (1 + (I/O耗时/CPU耗时)),一般可设置为2N。
143144

145+
### 关闭线程池
146+
147+
shutdown():
148+
149+
将线程池状态置为`SHUTDOWN`,并不会立即停止:
150+
151+
- 停止接收外部提交的任务
152+
- 内部正在跑的任务和队列里等待的任务,会执行完
153+
- 等到第二步完成后,才真正停止
154+
155+
shutdownNow():
156+
157+
将线程池状态置为`STOP`。企图立即停止,事实上不一定:
158+
159+
- 跟shutdown()一样,先停止接收外部提交的任务
160+
- 忽略队列里等待的任务
161+
- 尝试将正在跑的任务`interrupt`中断(不一定中断成功,取决于任务响应中断的逻辑)
162+
- 返回未执行的任务列表
163+
144164

145165

146166
## executor框架
@@ -931,42 +951,7 @@ worker5 finished the task
931951

932952
![](../img/thread-status.jpeg)
933953

934-
### join/yeild/sleep/wait/park
935-
936-
thread.join(),在main中创建了thread线程,在main中调用了thread.join()/thread.join(long millis),main线程放弃cpu控制权,线程进入WAITING/TIMED_WAITING状态,等到thread线程执行完才继续执行main线程。
937-
938-
Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
939-
940-
Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,让出cpu资源,但不释放对象锁,指定时间到后又恢复运行。作用:给其它线程执行机会的最佳方式。
941-
942-
obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。
943-
944-
obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
945-
946-
LockSupport.park()/LockSupport.parkNanos(long nanos)/LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。
947-
948-
949-
950-
### wait()和sleep()的区别
951-
952-
相同点:
953-
954-
1. 使当前线程暂停运行,把机会交给其他线程
955-
2. 任何线程在等待期间被中断都会抛出InterruptedException
956-
957-
不同点:
958-
959-
1. wait() 是Object超类中的方法;而sleep()是线程Thread类中的方法
960-
961-
2. 对锁的持有不同,wait()会释放锁,而sleep()并不释放锁
962-
963-
3. 唤醒方法不完全相同,wait() 依靠notify或者notifyAll 、中断、达到指定时间来唤醒;而sleep()到达指定时间被唤醒
964-
965-
4. 使用位置不同,wait只能在同步代码块或同步控制块中使用,而sleep可以在任何位置使用
966-
967-
968-
969-
## 中断
954+
### 中断
970955

971956
[线程中断](https://zhuanlan.zhihu.com/p/45667127)
972957

@@ -1004,6 +989,37 @@ private static void test2() {
1004989
}
1005990
```
1006991

992+
### join/yeild/sleep/wait/park
993+
994+
thread.join(),在main中创建了thread线程,在main中调用了thread.join()/thread.join(long millis),main线程放弃cpu控制权,线程进入WAITING/TIMED_WAITING状态,等到thread线程执行完才继续执行main线程。
995+
996+
Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
997+
998+
Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,让出cpu资源,但不释放对象锁,指定时间到后又恢复运行。作用:给其它线程执行机会的最佳方式。
999+
1000+
obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。
1001+
1002+
obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
1003+
1004+
LockSupport.park()/LockSupport.parkNanos(long nanos)/LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。
1005+
1006+
### wait()和sleep()的区别
1007+
1008+
相同点:
1009+
1010+
1. 使当前线程暂停运行,把机会交给其他线程
1011+
2. 任何线程在等待期间被中断都会抛出InterruptedException
1012+
1013+
不同点:
1014+
1015+
1. wait() 是Object超类中的方法;而sleep()是线程Thread类中的方法
1016+
1017+
2. 对锁的持有不同,wait()会释放锁,而sleep()并不释放锁
1018+
1019+
3. 唤醒方法不完全相同,wait() 依靠notify或者notifyAll 、中断、达到指定时间来唤醒;而sleep()到达指定时间被唤醒
1020+
1021+
4. 使用位置不同,wait只能在同步代码块或同步控制块中使用,而sleep可以在任何位置使用
1022+
10071023

10081024

10091025
## 原子类

中间件/Redis入门指南总结.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
- [RDB方式](#rdb%E6%96%B9%E5%BC%8F)
1818
- [AOF方式](#aof%E6%96%B9%E5%BC%8F)
1919
- [集群](#%E9%9B%86%E7%BE%A4)
20+
- [缓存穿透](#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F)
21+
- [缓存雪崩](#%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9)
22+
- [缓存击穿](#%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF)
2023

2124
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2225

@@ -425,3 +428,32 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
425428
- 首先,从数据库使用`SLAVE NO ONE`将从数据库提升为主数据库继续服务;
426429
- 启动奔溃的主数据库,通过`SLAVEOF`命令将其设置为新的主数据库的从数据库,即可将数据同步过来。
427430

431+
432+
433+
## 缓存穿透
434+
435+
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。
436+
437+
1. 缓存空值,不会查数据库
438+
2. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,查询不存在的数据会被 这个bitmap拦截掉,从而避免了对DB的查询压力。
439+
440+
布隆可以看成数据库的缩略版,用来判定是否存在值。启动的时候过滤器是要全表扫描的,数据库数据发生变化的时候会更新布隆过滤器。
441+
442+
443+
444+
## 缓存雪崩
445+
446+
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
447+
448+
1. 加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到DB。
449+
2. 在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
450+
451+
452+
453+
## 缓存击穿
454+
455+
缓存击穿:在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿。这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
456+
457+
1. 第一个查询数据的请求上使用一个互斥锁来锁住它(Redis的SETNX)。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
458+
459+
SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

其他/note.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
- [Maven的作用](#maven%E7%9A%84%E4%BD%9C%E7%94%A8)
1010
- [为什么使用消息队列](#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97)
1111
- [秒杀系统](#%E7%A7%92%E6%9D%80%E7%B3%BB%E7%BB%9F)
12+
- [shiro](#shiro)
13+
- [分布式锁](#%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81)
14+
- [dubbo](#dubbo)
15+
- [分布式ID](#%E5%88%86%E5%B8%83%E5%BC%8Fid)
16+
- [UUID](#uuid)
17+
- [数据库自增ID](#%E6%95%B0%E6%8D%AE%E5%BA%93%E8%87%AA%E5%A2%9Eid)
18+
- [基于Redis模式](#%E5%9F%BA%E4%BA%8Eredis%E6%A8%A1%E5%BC%8F)
19+
- [雪花算法](#%E9%9B%AA%E8%8A%B1%E7%AE%97%E6%B3%95)
1220

1321
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
1422

@@ -108,3 +116,73 @@ JWT只通过算法实现对Token合法性的验证,不依赖数据库,Memcac
108116

109117
用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去。
110118

119+
120+
121+
## shiro
122+
123+
作用:
124+
125+
1. 验证用户身份
126+
2. 用户访问权限控制
127+
3. 支持提供`Remember Me`服务
128+
129+
130+
131+
## 分布式锁
132+
133+
在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问,Java中我们一般可以使用synchronized语法和ReetrantLock去保证,这实际上是本地锁的方式。
134+
135+
在一个分布式系统中,多台机器上部署了多个服务,当客户端一个用户发起一个数据插入请求时,如果没有分布式锁机制保证,那么那多台机器上的多个服务可能进行并发插入操作,导致数据重复插入,对于某些不允许有多余数据的业务来说,这就会造成问题。而分布式锁机制就是为了解决类似这类问题,保证多个服务之间互斥的访问共享资源,如果一个服务抢占了分布式锁,其他服务没获取到锁,就不进行后续操作。
136+
137+
![](https://user-gold-cdn.xitu.io/2019/4/25/16a53749547937bb?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
138+
139+
实现分布式锁有以下几种方式:
140+
141+
- 基于数据库
142+
- 基于Redis
143+
- 基于zookeeper
144+
145+
146+
147+
## dubbo
148+
149+
分布式协调服务:在分布式系统中共享配置,协调锁资源,提供命名服务。
150+
151+
152+
153+
## 分布式ID
154+
155+
`全局唯一ID`就叫`分布式ID`
156+
157+
### UUID
158+
159+
```java
160+
String uuid = UUID.randomUUID().toString().replaceAll("-","");
161+
```
162+
163+
优点:
164+
165+
- 生成足够简单,本地生成无网络消耗,具有唯一性
166+
167+
缺点:
168+
169+
- 无序的字符串,不具备趋势自增特性
170+
- 没有具体的业务含义
171+
172+
### 数据库自增ID
173+
174+
优点:
175+
176+
- 简单方便,有序递增,方便排序和分页
177+
178+
缺点:
179+
180+
- 分库分表会带来问题,需要进行改造。
181+
182+
### 基于Redis模式
183+
184+
利用`redis``incr`命令实现ID的原子性自增。
185+
186+
### 雪花算法
187+
188+
![](https://user-gold-cdn.xitu.io/2020/2/16/1704bd6d27b09766?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)

工具/progit2.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
- [删除远程分支](#%E5%88%A0%E9%99%A4%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF)
6060
- [创建远程分支](#%E5%88%9B%E5%BB%BA%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF)
6161
- [cherry-pick](#cherry-pick)
62+
- [补丁](#%E8%A1%A5%E4%B8%81)
6263
- [同步fork项目的更新](#%E5%90%8C%E6%AD%A5fork%E9%A1%B9%E7%9B%AE%E7%9A%84%E6%9B%B4%E6%96%B0)
6364

6465
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -634,6 +635,12 @@ git branch -d tmp//如果不想保留temp分支 可以用这步删除
634635

635636
`git cherry_pick <start-commit-id>…<end-commit-id>`
636637

638+
### 补丁
639+
640+
Git apply 是必须从`git diff`中获得patch才能打上。需要自己重新 commit。
641+
642+
Git am是多了一个commit格式的邮件信息,这样的patch是从`git format–patch`获得的。保留 commit 信息。
643+
637644

638645

639646
## 同步fork项目的更新
@@ -662,3 +669,5 @@ git checkout master
662669
git rebase upstream/master
663670
```
664671

672+
673+

0 commit comments

Comments
 (0)