java
2025-03-26 23:21:25 0 举报
AI智能生成
核心内容描述: Java是一种面向对象的编程语言,以其平台无关性和稳固的安全特性而广受欢迎。它支持多线程,允许程序同时执行多个任务,而不会相互干扰。Java提供丰富的类库,从基础数据结构到网络通信、图形用户界面等,都有着完善的支撑。其运行时环境(JRE)和开发工具包(JDK)为开发者构建跨平台应用程序提供了所需的一切。 文件类型描述: Java的主要文件扩展名包括.java,这是源代码文件;.class,编译后的字节码文件;以及.jar,Java的归档文件,常用于打包多个类文件和相关的元数据和资源文件。 修饰语描述: Java不仅是一种强类型的、对象导向的语言,还拥有高度的可移植性和强大的异常处理机制。Java社区提供了广泛的开源框架和工具,如Spring和Hibernate,进一步增强了Java的应用范围和开发效率。总之,Java是业界标准之一,适合于构建企业级应用和嵌入式系统。
作者其他创作
大纲/内容
JavaSE零基础
环境搭建
常用dos命令及系统快捷键
Sublime Text或EditPlus的安装
通用文本编辑快捷键
安装JDK
计算机的组成
计算机语言发展史
Java语言发展史
Java语言特点
Java加载和执行的过程
第一个Java程序的开发
Java注释
public class和class的区别
基础语法
标识符
关键字
字面量
变量
数据类型
数据类型概述
数据类型作用
java中包括哪些数据类型
二进制
8种基本数据类型一览
整数型详解
编写代码时整数型的4种表示法
整数型字面量可以使用下划线分隔增强可读性(Java7新特性)
java中整数型字面量被默认当做int类型来处理,想以long的形式表示需要在字面量后添加L/l
int i = 10; (10是int类型,i是int类型,不存在类型转换)
long x = 10;(10是int类型,x是long类型,小容量可以自动转换成大容量,称为自动类型转换)
long y = 10L;(10L是long类型,y是long类型,不存在类型转换)
int z = 10L;(10L是long类型,z是int类型,大容量无法直接转换成小容量,编译报错,需要加强制类型转换符,int z = (int)10L;,这种操作叫做强制类型转换,谨慎使用可能损失精度)
long a = 2147483648;编译报错的原因,怎么修改
当整数型字面量没有超出byte,short,char取值范围时,可以直接赋值
原码、反码、补码,为什么byte b = (byte)200;的结果是:-56
浮点型详解
java开发中浮点型字面量默认被当做double来处理,想默认被当做float处理,需要在字面量后面添加F/f
float f = 2.0; 编译报错的原因,怎么修改
为什么java中不推荐使用双等号来判断两个浮点型数据是否相等
布尔型详解
java中布尔型字面量只有true和false,无其他值
布尔型变量主要使用在逻辑判断方面
字符型详解
字符编码是一张文字与二进制的转换对照表
'A' 'a' '0'这三个字符的ASCII码
字符编码的发展过程,以及开发常用的字符编码
java中规定字符char必须使用单引号括起来
java中的char可以存储一个汉字
java中的转义字符\t、\r、\n、\\、\'、\"、\u
基本数据类型转换规则
运算符
算术运算符
关系运算符
逻辑运算符
赋值运算符
字符串连接运算符
条件运算符
位运算符
控制语句
if
switch
for
while
do..while
break
continue
方法初步
方法重载/overload
方法重载有什么好处
什么时候考虑使用方法重载
满足什么条件时构成方法重载
演示方法重载在实际开发中的使用
方法执行时的内存变化
栈数据结构
JVM内存模型中有:栈空间
方法递归
什么是方法递归,怎么理解
方法递归没有结束条件会怎样
不使用递归计算n的阶乘
使用递归计算n的阶乘
方法递归时的内存图
多数情况下为什么建议使用循环代替递归
package与import
软件包的作用
包名命名规范
带包怎么编译和运行
import什么时候可以省略,什么时候必须使用
IntelliJ IDEA工具的使用
IDEA工具的安装与破解
IDEA工具怎么设置字体与字符集
IDEA中怎么创建Project
IDEA中怎么创建Module
Project与Module的关系
怎么创建package
怎么创建class
怎么快速生成main方法
怎么快速生成System.out.println()
怎么运行java程序
面向对象
面向过程和面向对象的区别
类和对象的概念
类的定义
对象的创建和使用
构造方法
怎么定义构造方法
怎么调用构造方法
缺省构造器
构造方法的作用
构造方法支持重载
当实例变量是一个引用的时候
面向对象三大特征
封装性
不封装有什么问题
怎么封装,代码怎么写
总结封装有什么作用
this关键字
方法调用时参数的传递
static关键字
static关键字概述
静态代码块
构造代码块
静态变量
静态方法
静态导入
空指针异常
类的继承
继承的作用
继承的基础语法
子类继承父类,在创建子类对象时,构造方法是如何执行的
方法覆盖/方法重写/override
回顾方法重载overload
什么时候考虑方法覆盖
满足什么条件时构成方法覆盖
super关键字
多态
多态的基础语法
多态在开发中的作用
Java并发
Tools组件
**同步工具**
CountDownLatch(闭锁)
CyclicBarrier(循环栅栏)
Semaphore(信号量)
Exchanger(交换器)
**线程工厂**
Executors(线程池工厂)
Locks框架
核心接口
Lock(显式锁)
ReadWriteLock(读写锁)
Condition(条件变量)
实现类
ReentrantLock(可重入锁)
ReentrantReadWriteLock(读写锁实现)
基础支撑
LockSupport(线程阻塞工具)
Atomic原子类
基本类型
AtomicBoolean
AtomicInteger
AtomicLong
数组类型
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
引用类型
AtomicReference
AtomicStampedReference(带版本号)
AtomicMarkableReference(带标记位)
字段更新器
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
并发集合
队列体系
**BlockingQueue(阻塞队列)**
ArrayBlockingQueue(数组实现)
LinkedBlockingQueue(链表实现)
PriorityBlockingQueue(优先级队列)
**Deque(双端队列)**
ArrayDeque
LinkedList
线程安全集合
ConcurrentMap(并发映射)
ConcurrentHashMap
CopyOnWriteArrayList(写时复制列表)
CopyOnWriteArraySet(写时复制集合)
ConcurrentSkipListSet(跳表集合)
Executor框架
执行体系
Executor(执行器接口)
ExecutorService(服务扩展)
ScheduledExecutorService(定时任务)
异步计算
Future(异步结果)
RunnableFuture
ScheduledFuture
Callable(带返回值任务)
任务控制
CompletionService(完成服务)
RejectedExecutionHandler(拒绝策略)
时间单位
TimeUnit(时间粒度控制)
java集合
核心接口
迭代体系
**Iterator**
→ ListIterator(扩展迭代功能)
集合根接口
**Collection**
→ List(有序集合)
→ Set(唯一性集合)
→ Queue(队列结构)
→ Deque(双端队列)
映射体系
**Map**
→ SortedMap(有序映射)
→ NavigableMap(导航映射)
特殊接口
**Enumeration**(旧版迭代接口)
**SortedSet**(有序集合)
→ NavigableSet(导航集合)
抽象实现层
集合抽象类
**AbstractCollection**
→ AbstractList(列表基础实现)
→ AbstractSequentialList(顺序访问列表)
→ AbstractSet(集合基础实现)
映射抽象类
**AbstractMap**
→ HashMap(哈希表实现)
→ TreeMap(红黑树实现)
→ WeakHashMap(弱引用映射)
具体实现类
List系实现
**ArrayList**(基于数组)
底层:数组
特点:查询快(`getter()`/`setter()`高效),增删慢
线程不安全
扩容机制:容量不足时,扩容为当前容量 *1.5 + 1
继承:`AbstractList<E>`
实现:`List<E>`、`RandomAccess`
**LinkedList**(基于链表)
底层:双向循环链表
特点:查询慢,增删快(`add()`/`remove()`高效)
线程不安全
继承:`AbstractSequentialList<E>`
实现:`List<E>`、`Deque<E>`
**Vector**(线程安全列表)
底层:数组
特点:查询快,增删慢
线程安全(效率低)
扩容机制:容量不足时,默认扩展一倍
继承:`AbstractList<E>`
实现:`List<E>`、`RandomAccess`
→ Stack(栈结构)
并发实现
`CopyOnWriteArrayList<E>`
直接实现:`List<E>`、`RandomAccess`
特性:写时复制、读无锁
Set系实现
**`Set<E>`**(接口)
继承自:`Collection<E>`
子接口:
**`SortedSet<E>`**
子接口:**`NavigableSet<E>`**
抽象类
**`AbstractCollection<E>`**
实现接口:`Collection<E>`
**`AbstractSet<E>`**
继承自:`AbstractCollection<E>`
实现接口:`Set<E>`
具体实现类
**`HashSet<E>`**
继承自:`AbstractSet<E>`
实现接口:`Set<E>`
底层:哈希表(基于 HashMap 实现)
特点:存取速度快
允许 `null` 键/值
**`LinkedHashSet<E>`**
继承自:`HashSet<E>`
实现接口:`Set<E>`
**`CopyOnWriteArraySet<E>`**
继承自:`AbstractSet<E>`
实现接口:`Serializable`
**`ConcurrentSkipListSet<E>`**
继承自:`AbstractSet<E>`
实现接口:`NavigableSet<E>`
**`TreeSet<E>`**
继承自:`AbstractSet<E>`(间接实现 `NavigableSet<E>`)
底层:基于 `TreeMap` 的 `SortedSet`
底层:二叉树(基于 TreeMap 的 SortedSet 实现)
特点:排序存储
其他说明
**`NavigableSet<E>`**:支持导航方法(如 `ceiling()`, `floor()`)。
**线程安全类**:
`CopyOnWriteArraySet`:写时复制技术,适用于读多写少场景。
`ConcurrentSkipListSet`:基于跳表实现,支持高并发。
历史实现
**Hashtable**(同步哈希表)
← Dictionary(旧版键值存储)
底层:哈希表 + 双向链表(基于 LinkedHashMap 实现)
特点:按插入顺序存储
工具类
`Arrays`(数组工具)
`Collections`(集合工具)
类关系说明
1. 实线箭头表示继承关系
2. 虚线箭头表示依赖关系
3. 工具类使用虚线框特殊标注
4. Navigable*接口提供导航方法(如floor/ceiling)
5. Abstract*类提供骨架实现
Map 相关特性(键不可重复,值可重复)
**通用特性**
线程不安全
允许 `null` 键/值(部分实现类例外)
**特殊实现类**
线程安全实现(如 `ConcurrentHashMap`):
键/值均不允许 `null`
有序实现(如 `TreeMap`):
底层:二叉树
核心接口
**Map<K,V>**(接口)
映射体系根接口,定义基本键值对操作
抽象结构层
字典抽象
**Dictionary<K,V>**(抽象类)
早期键值存储抽象,已被Map接口取代
有序映射
**SortedMap<K,V>**(接口)
扩展排序功能
→ **NavigableMap<K,V>**(接口)
新增导航方法(如floorEntry/ceilingKey)
具体实现类
基础实现
**Hashtable<K,V>**
(继承Dictionary,实现Map)
线程安全哈希表,已过时
**HashMap<K,V>**
(继承AbstractMap,实现Map)
数组+链表/红黑树结构,非线程安全
特殊实现
**WeakHashMap<K,V>**
弱引用键哈希表,自动清理失效条目
**IdentityHashMap<K,V>**
使用==比较键对象
**EnumMap<K extends Enum<K>,V>**
针对枚举类型优化的高性能映射
并发实现
**ConcurrentHashMap<K,V>**
分段锁技术,高并发场景首选
**ConcurrentSkipListMap<K,V>**
跳表结构,支持并发排序访问
有序实现
**TreeMap<K,V>**
红黑树实现,自然序或Comparator排序
扩展实现
**LinkedHashMap<K,V>**
(继承HashMap)
双向链表维护插入/访问顺序
类关系说明
1. **继承关系**:
`类 → 父类` 表示继承,`类 ⇢ 接口` 表示实现
2. **颜色标识**:
绿色:接口(Map/SortedMap/NavigableMap)
黄色:抽象类(Dictionary/AbstractMap)
蓝色:具体实现类
3. **重要特性**:
所有具体类均实现Serializable支持序列化
HashMap及其子类实现Cloneable
Concurrent系列实现线程安全
最佳实践
单线程环境优先使用HashMap
并发场景选择ConcurrentHashMap
需要排序时使用TreeMap
LRU缓存实现可继承LinkedHashMap
IO
Type
BIO
Block IO , 顾名思义 , 同步阻塞
客户端socket发送一个请求,服务端socket进行处理后返回响应,相应必须是等处理完才会返回
在这之前啥也干不了,这可不就是同步么
这种方式最大的坑在于,每次一个客户端接入,都是要服务端创建一个线程来服务这个客户端的
如果有大量客户端的时候,那么服务端的线程数量可能达到几千、几万甚至几十万
然后服务器BOOM
要么搞个线程池,固定数量的线程池来处理
但是高并发又会有各种排队和延时的问题
图示
分支主题
NIO
New IO , 同步非阻塞,基于Reactor模型
Buffer缓冲区,将数据写入Buffer中,然后从Buffer中读取数据
IntBuffer、LongBuffer、CharBuffer等很多种针对基础数据类型的Buffer
Channel,NIO中都是通过Channel来进行数据读写的
Selector,多路复用器
selector会不断轮询注册的channel,如果某个channel上发生了读写时间,selector就会将这些channel获取出来
通过SelectionKey获取有读写时间的channel,就可以进行IO操作
一个Selector就通过一个线程,就可以轮询成千上万个channel,这就意味着你的服务端可以介入成千上万的客户端
核心即非阻塞,selector一个线程就可以不停的轮询缠你了,所有客户端请求都不会阻塞,直接就会进来,大不了排下队
NIO的优化思想就是一个请求一个线程,只有某个客户端发送了一个请求的时候,才会启动一个线程来处理
处理时还是要先读取数据,处理,再返回的,这是个同步的过程
IO磁盘操作时需不断的while询问CPU是否处理完成
图示
分支主题
AIO
Async IO, 异步非阻塞,基于Proactor模型
每个连接发送过来的请求,都会绑定一个buffer,然后通知操作系统去一部完成读
此时你的程序是回去干别的事儿的,等操作系统完成数据读取后,就会回调你的接口,给你操作系统异步读完的数据
然后你对这个数据处理一下,接着讲结果往回写
写的时候也是个操作系统一个buffer,让操作系统自己获取数据去完成写操作,写完以后再回来通知你
工作线程
读取数据的时候,提供给操作系统一个buffer,空的,然后你就可以干别的事儿了,把读数据的事儿交给系统去干
内核读数据将数据放入buffer,完事了,来回调你的一个接口,告诉你说,ok,数据读好了
写同理
同步、非同步、阻塞、非阻塞
BIO
用BIO的流读写文件时,你发起个IO请求直接hang死,必须等搞完了这次IO才能返回
这个针对的是磁盘文件的IO读写
FileInputStream ,BIO,卡在那儿,直到你读写完成了才可以
NIO
通过NIO的FileChannel发起个文件IO操作,其实发起之后就反悔了,你可以干别的事儿,这就是非阻塞
但是接下来你还得不断地去轮询操作系统,看IO操作完事儿了没有
AIO
通过AIO发起个文件IO操作之后,你立马就可以返回干别的事儿了,接下来你也不用管了
操作系统自己干完了IO之后,告诉你说ok了。
同步就是你自己还得主动去轮询操作系统,异步就是操作系统反过来通知你
零拷贝
先来看看普通的一次IO操作吧
File file = new File("XXX.txt");
RandomAccessFile raf = new RandomAccessFile(file,"rw");
byte[] arr = new byte[(int)file.length()];
raf.read(arr);
Socket socket = new ServerSocket(8080).accept();
socket.getOutputStreadm().write(arr);
用户态 -> 内核态 && 磁盘数据 -> DMA引擎拷贝 -> 内核缓冲区
内核态 -> 用户态 && 内核缓冲区数据 -> CPU拷贝 -> 用户缓冲区
用户态 -> 内核态 && 用户缓冲区数据 -> CPU拷贝 -> Socket缓冲区
异步化
Socket缓冲区数据 -> DMA引擎拷贝 -> 网络协议引擎
内核态 -> 用户态
图示
分支主题
mmap内存映射技术
直接将磁盘文件数据映射到内核缓冲区,这个映射过程是基于DMA拷贝的
同时用户缓冲区是跟内核缓冲区贡献一块映射数据的,建立共享映射之后
就不需要从内核缓冲区拷贝到用户缓冲区了
即减少了一次数据拷贝
图示
分支主题
所谓零拷贝
基于linux提供的sendfile,也就是零拷贝技术
用户态 -> 内核态 , 磁盘数据 -> DMA拷贝 -> 内核缓冲区,同时从内核缓冲区拷贝一些offset和length到Socket缓冲区
内核态 -> 用户态 ,内核缓冲区数据 -> DMA拷贝 -> 网络协议引擎 , 同时从Socket缓冲区拷贝一些offset和length到网络协议引擎中
这个offset和length的量很少,几乎可以忽略不计
图示
分支主题
并发编程
内存模型
主内存 data = 0
data = 0
工作内存
(cpu级别 data = 0)
分支主题
线程1
data ++
分支主题
分支主题
data = 0
工作内存
(cpu级别 data = 0)
分支主题
线程2
data ++
说说并发编程可能存在哪些问题吧
原子性
data++,必须是独立执行的,没有人影响我的,一定是我自己执行成功之后,别人才能来进行下一次data++的执行
Java原生支持int=0这种基本类型赋值是原子性的
可见性
有序性
具备有序性,不会发生指令重排导致我们的代码异常;不具备有序性,可能会发生一些指令重排,导致代码可能会出现一些问题
synchronized
浅谈synchronized
monitor
monitorenter
// 代码对应指令
monitorexit
每个类/对象都有一个关联的monitor,如果要对对象加锁,那么必须先这个对象获取关联monitor的lock锁
monitor里面有一个计数器,从0开始
如果一个线程要获取monitor的锁,那么就要先看这个计数器是不是0
如果是0,那么说明没人获取锁,他可以获取锁,然后对计数器 加 1
支持重入锁
PS :
synchronized(myObject){ // 类的class对象来走的
// 一大堆代码
synchronized(myObject){
// 一大堆代码
}
}
如果不是0,那么说明有其他线程获取到锁了,那么它就什么事也干不了,只能先等着获取锁
接着如果出了synchronized修饰的代码片段,会执行monitorexit指令
此时获取锁的线程就会对那个对象的monitor里的计数器减 1,如果有多次重入加锁,那就多次减 1 ,直至减为0
此时锁被释放,其他阻塞住的线程可以重新请求获取锁
只有一个线程能成功获取锁
可以保证原子性、有序性、可见性
原子性
加锁和释放锁,ObjectMonitor
可见性
加锁,在进入synchronized代码块时的读操作,都会强制执行reflush
Load内存屏障
释放锁,在出代码块时,代码块内所有的写操作,都会强制执行flush操作
Stroe内存屏障
有序性
通过加各种内存屏障,保证有序性
代码块内部不保证有序性
但是同步代码块内部的指令和外部的指令,是不能重排的
核心原理示意图
分支主题
很简单,JDk 1.6之后,对synchronized内的加锁机制做了大量的优化,这里就是优化为CAS加锁的
你在之前把ReentrantLock底层的源码都读懂了,AQS的机制都读懂了之后,那么synchronized底层的实现差不多的
synchronized的ObjectMonitor的地位就跟ReentrantLock里的AQS是差不多的
对比
synchronized和locks包的锁有什么不同
其实锁的实现原理都是一个目的,让所有线程看到某种标记
synchronized是通过在对象头设置一个标记。上面加一个mark word
是一种JVM原生的锁实现方式
ReentrantLock以及所有基于Lock接口的实现类,都是通过一个被volatile修饰的int型变量
并保证对所有线程都能拥有对该int的可见性和原子修改,其本质是基于所谓的AQS框架
如何选择
Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现的
synchronized在发生异常时,会自动释放线程粘有的锁,因此不会导致死锁现象的发生
而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此需要再finally块中释放锁
Lock可以让等待锁的线程响应中断,而synchronized却不行
使用synchronized时,等待的线程会一直等待下去,不能响应中断
通过Lock可以知道有没有成功获取锁,而synchronized却无法办到
Lock可以提高多个线程进行读操作的效率
从性能上来说,如果竞争资源不激烈,两者的性能是差不多的
而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized
类似zk的羊群效应?Curator对zk锁的优化类似?
总结
类锁所有对象一把锁 对象锁一个对象一把锁,多个对象多把锁
类锁:
SynchronizedTest.init();
public synchronized static void init(){}
synchronized (MyService.class){}
对象锁:
SynchronizedTest synchronizedTest = new SynchronizedTest();
synchronizedTest.init();
public synchronized void init() {}
synchronized (synchronizedTest){}
1.6以后的锁优化
锁消除
锁消除是JIT编译器对synchronized锁做的优化,在编译的时候,JIT会通过逃逸分析技术,来分析synchronized锁对象
是不是只可能被一个线程来加锁,没有其他的线程来竞争加锁,这个时候编译就不用加入monitorenter和monitorexit的指令
锁粗化
这个意思就是,JIT编译器如果发现有代码里连续多次加锁释放锁的代码,会给合并为一个锁,就是锁粗化,把一个锁给搞粗了,避免频繁多次加锁释放锁
偏向锁
monitorenter和monitorexit是要使用CAS操作加锁和释放锁的,开销较大,因此如果发现大概率只有一个线程会主要竞争一个锁,那么会给这个锁维护一个偏好(Bias),后面他加锁和释放锁,基于Bias来执行,不需要通过CAS
但是如果有偏好之外的线程来竞争锁,就要收回之前分配的偏好
J V M会利用C A S操作,在对象头上的M a r kW o r d部分设置线程I D,以表示这个对象偏向于当前线程,所以并不涉及真正的互斥锁,因为在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竞争开销。
轻量级锁
如果偏向锁没能成功实现,就是因为不同线程竞争锁太频繁了,此时就会尝试采用轻量级锁的方式来加锁,就是将对象头的Mark Word里有一个轻量级锁指针,尝试指向持有锁的线程,然后判断一下是不是自己加的锁
如果是自己加的锁,那就执行代码就好了
如果不是自己加的锁,那就是加锁失败,说明有其他人加了锁,这个时候就是升级为重量级锁
自适应性锁
自旋锁
你这个锁里面的代码实际执行的非常快
当其他线程获取锁未成功时,不切换线程,自旋一会,等待你这个锁释放,减少上下文切换带来的性能消耗
CAS
即compareAndSet
多线程同时读取主内存中的数据,必然会导致并发安全问题
优点
CAS即基于底层硬件实现,给你保证一定是原子性的
即同一时间只有一个线程可以成功执行CAS
先比较再设置
其他线程执行CAS会失败
并发包下AtomicInteger等类天然支持CAS保证原子性
缺点
只能保证一个变量的原子操作
长时间自旋,开销大
存在ABA问题
AQS
AbstractQueueSynchronizer
抽象队列同步器
ReentrantLock
线程1
CAS更新 state = 1
分支主题
分支主题
分支主题
state = 0 - > state = 1
线程1
线程2
CAS更新 state = 1
分支主题
分支主题
分支主题
分支主题
等待队列
线程2
ReentrantLock源码分析
new ReentrantLock()
NonfairSync - > 非公平锁
lock()
直接尝试获取锁
未获取锁成功,再执行acquire()方法
tryAcquire()尝试获取锁
直接尝试获取锁
未获取到锁,看下当前拿到锁的线程是不是自己,是自己则state + 1重入
new ReentrantLock(true)
FairSync - > 公平锁
lock()
acquire()
tryAcquire()尝试获取锁
看看队列里是否有人排队
没人排队的话再尝试获取锁
未获取到锁,看下当前拿到锁的线程是不是自己,是自己则state + 1重入
volatile
用来解决可见性和有序性的
在有些罕见的条件下,可以保证原子性 (double / floot)
32位虚拟机中,对于这种64位的操作,可能会有高32位、低32位并发写的问题,volatile是能保证这种数据的原子性的
加了volatile关键字修饰的参数,在读写的时候会强制执行flush和reflush操作
然后通过总线嗅探机制,保证其他线程的可见性
内存屏障
Load
Store
Acquire
Release
等等,这个不用细扣。各个硬件底层的实现都是不一样的,没有统一的说法
happen-before原则
即规定了在某些条件下,不允许编译器、指令器对你写的代码进行指令重排,以此来保证有序性
程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作,比如说在代码里有先对一个lock.lock(),lock.unlock(),lock.lock()
volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个volatile变量的读操作,volatile变量写,再是读,必须保证是先写,再读
传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作,thread.start(),thread.interrupt()
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
ThreadLocal
为什么有内存泄漏问题?
底层代码ThreadLocalMap - > K-V,其中key是一个内部静态类继承了WeakReference
即弱引用,弱引用在gc的时候会被直接清理掉
导致有null-value这样的数据大量存在,占用内存空间导致内存泄漏
Java团队做了什么优化?
你在通过ThreadLocal , set、get、remove时,他会自动清理掉map里null为key的
确保不会有很多的null值引用了你的value造成内存的泄漏问题
平时使用ThreadLocal需要注意什么?
尽量避免在 ThradLocal长时间放入数据,不使用时最好及时进行remove,自己主动把数据删除
硬件级别MESI协议
重排
指定重排
javac静态编译器编译成.class文件时指令重排
JIT动态编译.class文件未机器码的时候指令重排
处理器执行指令时的无序处理,比如指令1 指令2 指令3执行顺序可能为1,3,2
内存重排序,当指令写入到硬件组件后(写缓存 高速缓存 无效队列)可能发生重排序
处理器的重排序
指令乱序放入到高速缓存中
猜测执行,比如if语句先不执行条件去执行满足条件里面的逻辑最后在执行条件
可能造成可见性问题的组件
寄存器
写缓冲器
高速缓存
处理可见性问题的操作
flush
将无效队列中invalid message刷新到高速缓存让数据无效强制从其他处理器的高速缓存/主内存中读取
reflush
强制将写缓冲区中数据刷新到高速缓存/主内存中
高速缓存底层的数据结构
拉链散列表多个bucket组成
index确定所在bucket
tag定位cache entry
offset当前缓存变量的偏移量
每个bucket挂多个cache entry
每个cache entry包含三部分
tag:当前缓存行指向主存中数据的地址值
cache line:缓存数据
flag:数据状态 s:共享 invalidate:无效 exclusive:独占式 Modify:修改
读写流程、原理
多个处理器高速缓存通过总线相连(存在问题:多个写操作阻塞 需要等待其他处理器ack)
给处理器01向总线发送read请求读取数据
总线从主存中读取数据给处理器01
如果数据被多个处理器共享则flag标识为s状态
当变量被修改时,处理器01会往总线发送一个invalidate message消息,等待其他处理器回复ack invalidate消息
所有处理器都返回ack后获取数据修改的独占锁,修改数据flag=exclusive,修改数据完成后为modify状态
此时其他处理器中数据为invalidate状态,其他处理器从处理器01的高速缓存或者主存中读取数据
原理图示意
分支主题
优化后
优化多个写操作阻塞:
写数据不等待invalidate ack直接写入到写缓冲区中
其他处理器收到invalidate message后直接写入到无效队列中返回ack
处理器01嗅探到invalidate ack消息后从写缓冲区刷新数据到高速缓存中
原理图示意
分支主题
线程池
为什么要使用线程池
线程的创建和销毁的代价很大
有效控制线程数量,避免创建过多线程
内部组成
线程管理器(ThreadPool)
这个就是负责创建、销毁线程池的
工作线程(PoolWorker)
就是线程池中的一个线程
工作任务(Task)
就是线程池中某个线程的业务代码实现
任务队列(TaskQueue)
这个是扔到线程池里的任务需要排队的队列
常见的线程池
SingleThreadExecutor
单线程池队列
里面就一个线程,然后慢慢去消费。
FixedThreadExecutor
固定数量线程池
根据你设定的线程数量执行,多出来的进入队列排队等待
比如说,线程池里面固定就100个线程,超过这个线程数就到队列里面去排队等待
适用于负载比较均衡的情况
CachedThreadExecutor
自动回收线程池
无论多少任务,根据你的需要任意的创建线程,最短的时间满足你
适用于存在高峰的情况 | ps : 容易崩掉 ,4核8GB的100个线程就差不多了,cpu负载可能就 70%/80%了
高峰过去后,大量线程处于空闲状态,等待60s就会被销毁掉了
ScheduleThreadExecutor
定时任务线程池
线程数量无限制,定时调度执行任务
各种组件源码中常用,比如eureka、rocketmq的心跳等等
常用API
Executor
代表线程池的接口,有个execute()方法,扔进去一个Runnable类型对象,就可以分配一个线程给你执行
ExecutorService
这是Executor的子接口,相当于是一个线程池的接口,有销毁线程池等方法
Executors
线程池的辅助工具类,辅助入口类,可以根据Executors快速创建你需要的线程池
ThreadPoolExecutor
这是ExecutorService的实现类,这才是正儿八经代表一个线程池的类
一般在Executors里创建线程池的时候,内部都是直接创建一个ThreadPoolExecutor的实例对象返回的,然后同时给设置了各种默认参数
Executor
源码
分支主题
实现
分支主题
核心参数配置
corePoolSize
线程池里的核心线程数量
maximumPoolSize
线程池里允许的最大线程数量
keepAliveTime
等待时间,corePoolSize外的线程等待时间大于这个值,则会被清理掉
unit
keepAliveTime的单位
workQueue
工作队列,当前运行的线程数 > corePoolSizes时,多出来的线程进入queue中等待
threadFactory
如果有新的线程需要创建时,就是由这个线程池来进行创建的
handle
线程数超过maximumPoolSize并且queue满了的时候,仍有线程进来所执行的策略
默认直接报错
启动原理示意图
分支主题
Java并发体系
Java内存模型(JMM)
线程通信机制
内存共享
Java采用
消息传递
内存模型
重排序
为了程序的性能,处理器、编译器都会对程序进行重排序处理
条件
在单线程环境下不能改变程序运行的结果
存在数据依赖关系的不允许重排序
问题
重排序在多线程环境下可能会导致数据不安全
顺序一致性
多线程环境下的理论参考模型
为程序提供了极强的内存可见性保证
特性
一个线程中的所有操作必须按照程序的顺序来执行
所有线程都只能看到一个单一的操作执行顺序,不管程序是否同步
每个操作都必须原子执行且立刻对所有线程可见
happens-before
JMM中最核心的理论,保证内存可见性
在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。
理论
如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前
两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法
as-if-serial
所有的操作均可以为了优化而被重排序,但是你必须要保证重排序后执行的结果不能被改变
synchronized
同步、重量级锁
原理
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
锁对象
普通同步方法,锁是当前实例对象
静态同步方法,锁是当前类的class对象
同步方法块,锁是括号里面的对象
实现机制
Java对象头
synchronized的锁就是保存在Java对象头中的
包括两部分数据
Mark Word(标记字段)
Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据,它会根据对象的状态复用自己的存储空间
包括
哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳
Klass Pointer(类型指针)
monitor
Owner
初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL
锁优化
自旋锁
该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁(循环方式)
自旋字数较难控制(-XX:preBlockSpin)
存在理论:线程的频繁挂起、唤醒负担较重,可以认为每个线程占有锁的时间很短,线程挂起再唤醒得不偿失
缺点
自旋次数无法确定
适应性自旋锁
自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定
自旋成功,则可以增加自旋次数,如果获取锁经常失败,那么自旋次数会减少
锁消除
若不存在数据竞争的情况,JVM会消除锁机制
判断依据
变量逃逸
锁粗化
将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。例如for循环内部获取锁
轻量级锁
在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗
通过CAS来获取锁和释放锁
性能依据
对于绝大部分的锁,在整个生命周期内都是不会存在竞争的
缺点
在多线程环境下,其运行效率比重量级锁还会慢
偏向锁
为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径
主要尽可能避免不必须要的CAS操作,如果竞争锁失败,则升级为轻量级锁
volatile
特性
volatile可见性:对一个volatile的读,总可以看到对这个变量最终的写
volatile原子性:volatile对单个读/写具有原子性(32位Long、Double),但是复合操作除外,例如i++;
实现机制
内存屏障
内存语义
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新到主内存中
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量
操作系统语义
主存、高速缓存(线程私有)缓存一致?
解决方案
通过在总线加LOCK#锁的方式
通过缓存一致性协议(MESI协议)
内存模型
重排序
happens-before
DCL
单例模式
DCL
重排序
happens-beofre
解决方案
volatile方案
禁止重排序
基于类初始化的解决方案
利用classloder的机制来保证初始化instance时只有一个线程。JVM在类初始化阶段会获取一个锁,这个锁可以同步多个线程对同一个类的初始化
并发基础
AQS
AbstractQueuedSynchronizer,同步器,实现JUC核心基础组件
解决了子类实现同步器时涉及的大量细节问题,例如获取同步状态、FIFO同步队列
采用模板方法模式,AQS实现大量通用方法,子类通过继承方式实现其抽象方法来管理同步状态
CLH同步队列
FIFO双向队列,AQS依赖它来解决同步状态的管理问题
首节点唤醒,等待队列加入到CLH同步队列的尾部
同步状态获取与释放
独占式
获取锁
获取同步状态:acquire
响应中断:acquireInterruptibly
超时获取:tryAcquireNanos
释放锁
release
共享式
获取锁
acquireShared
释放锁
releaseShared
线程阻塞和唤醒
当有线程获取锁了,其他再次获取时需要阻塞,当线程释放锁后,AQS负责唤醒线程
LockSupport
是用来创建锁和其他同步类的基本线程阻塞原语
每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在进程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用
park()、unpark()
CAS
Compare And Swap,整个JUC体系最核心、最基础理论
内存值V、旧的预期值A、要更新的值B,当且仅当内存值V的值等于旧的预期值A时才会将内存值V的值修改为B,否则什么都不干
native中存在四个参数
缺陷
循环时间太长
只能保证一个共享变量原子操作
ABA问题
解决方案
版本号
AtomicStampedReference
锁
ReentrantLock
可重入锁,是一种递归无阻塞的同步机制
比synchronized更强大、灵活的锁机制,可以减少死锁发生的概率
分为公平锁、非公平锁
底层采用AQS实现,通过内部Sync继承AQS
ReentrantReadWriteLock
读写锁,两把锁:共享锁:读锁、排他锁:写锁
支持公平性、非公平性,可重入和锁降级
锁降级:遵循获取写锁、获取读锁在释放写锁的次序,写锁能够降级成为读锁
Condition
Lock 提供条件Condition,对线程的等待、唤醒操作更加详细和灵活
内部维护一个Condition队列。当前线程调用await()方法,将会以当前线程构造成一个节点(Node),并将节点加入到该队列的尾部
并发工具类
CyclicBarrier
它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
通俗讲:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活
底层采用ReentrantLock + Condition实现
应用场景
多线程结果合并的操作,用于多线程计算数据,最后合并计算结果的应用场景
CountDownLatch
在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
与CyclicBarrier区别
CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待
CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier
内部采用共享锁来实现
Semaphore
信号量
一个控制访问多个共享资源的计数器
从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动
信号量Semaphore是一个非负整数(>=1)。当一个线程想要访问某个共享资源时,它必须要先获取Semaphore,当Semaphore >0时,获取该资源并使Semaphore – 1。如果Semaphore值 = 0,则表示全部的共享资源已经被其他线程全部占用,线程必须要等待其他线程释放资源。当线程释放资源时,Semaphore则+1
应用场景
通常用于限制可以访问某些资源(物理或逻辑的)的线程数目
内部采用共享锁实现
Exchanger
可以在对中对元素进行配对和交换的线程的同步点
允许在并发任务之间交换数据。具体来说,Exchanger类允许在两个线程之间定义同步点。当两个线程都到达同步点时,他们交换数据结构,因此第一个线程的数据结构进入到第二个线程中,第二个线程的数据结构进入到第一个线程中
其他
ThreadLocal
一种解决多线程环境下成员变量的问题的方案,但是与线程同步无关。其思路是为每一个线程创建一个单独的变量副本,从而每个线程都可以独立地改变自己所拥有的变量副本,而不会影响其他线程所对应的副本
ThreadLocal 不是用于解决共享变量的问题的,也不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制
四个方法
get():返回此线程局部变量的当前线程副本中的值
initialValue():返回此线程局部变量的当前线程的“初始值”
remove():移除此线程局部变量当前线程的值
set(T value):将此线程局部变量的当前线程副本中的值设置为指定值
ThreadLocalMap
实现线程隔离机制的关键
每个Thread内部都有一个ThreadLocal.ThreadLocalMap类型的成员变量,该成员变量用来存储实际的ThreadLocal变量副本。
提供了一种用键值对方式存储每一个线程的变量副本的方法,key为当前ThreadLocal对象,value则是对应线程的变量副本
注意点
ThreadLocal实例本身是不存储值,它只是提供了一个在当前线程中找到副本值得key
是ThreadLocal包含在Thread中,而不是Thread包含在ThreadLocal中
内存泄漏问题
ThreadLocalMap
key 弱引用 value 强引用,无法回收
显示调用remove()
Fork/Join
一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架
核心思想
“分治”
fork分解任务,join收集数据
工作窃取
某个线程从其他队列里窃取任务来执行
执行块的线程帮助执行慢的线程执行任务,提升整个任务效率
队列要采用双向队列
核心类
ForkJoinPool
执行任务的线程池
ForkJoinTask
表示任务,用于ForkJoinPool的任务抽象
ForkJoinWorkerThread
执行任务的工作线程
Java并发集合
ConcurrentHashMap
CAS + Synchronized 来保证并发更新的安全,底层采用数组+链表/红黑树的存储结构
重要内部类
Node
key-value键值对
TreeNode
红黑树节点
TreeBin
就相当于一颗红黑树,其构造方法其实就是构造红黑树的过程
ForwardingNode
辅助节点,用于ConcurrentHashMap扩容操作
sizeCtl
控制标识符,用来控制table初始化和扩容操作的
含义
负数代表正在进行初始化或扩容操作
-1代表正在初始化
-N 表示有N-1个线程正在进行扩容操作
正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小
重要操作
initTable
ConcurrentHashMap初始化方法
只能有一个线程参与初始化过程,其他线程必须挂起
构造函数不做初始化过程,初始化真正是在put操作触发
步骤
sizeCtl < 0 表示正在进行初始化,线程挂起
线程获取初始化资格(CAS(SIZECTL, sc, -1))进行初始化过程
初始化步骤完成后,设置sizeCtl = 0.75 * n(下一次扩容阈值),表示下一次扩容的大小
put
核心思想
根据hash值计算节点插入在table的位置,如果该位置为空,则直接插入,否则插入到链表或者树中
真实情况较为复杂
步骤
table为null,线程进入初始化步骤,如果有其他线程正在初始化,该线程挂起
如果插入的当前 i 位置 为null,说明该位置是第一次插入,利用CAS插入节点即可,插入成功,则调用addCount判断是否需要扩容。若插入失败,则继续匹配(自旋)
若该节点的hash ==MOVED(-1),表示有线程正在进行扩容,则进入扩容进程中
其余情况就是按照链表或者红黑树结构插入节点,但是这个过程需要加锁(synchronized)
get
步骤
table ==null ;return null
从链表/红黑树节点获取
扩容
多线程扩容
步骤
构建一个nextTable,其大小为原来大小的两倍,这个步骤是在单线程环境下完成的
将原来table里面的内容复制到nextTable中,这个步骤是允许多线程操作
链表转换为红黑树过程
所在链表的元素个数达到了阈值 8,则将链表转换为红黑树
红黑树算法
1.8 与 1.7的区别
ConcurrentLinkedQueue
基于链接节点的无边界的线程安全队列,采用FIFO原则对元素进行排序,内部采用CAS算法实现
不变性
在入队的最后一个元素的next为null
队列中所有未删除的节点的item都不能为null且都能从head节点遍历到
对于要删除的节点,不是直接将其设置为null,而是先将其item域设置为null(迭代器会跳过item为null的节点)
允许head和tail更新滞后。这是什么意思呢?意思就说是head、tail不总是指向第一个元素和最后一个元素(后面阐述)
head的不变性和可变性
tail的不变性和可变性
精妙之处:利用CAS来完成数据操作,同时允许队列的不一致性,弱一致性表现淋漓尽致
ConcurrentSkipListMap
第三种key-value数据结构:SkipList(跳表)
SkipList
平衡二叉树结构
Skip list让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,通过“空间来换取时间”的一个算法,在每个节点中增加了向前的指针,在插入、删除、查找时可以忽略一些不可能涉及到的结点,从而提高了效率
特性
由很多层结构组成,level是通过一定的概率随机产生的
每一层都是一个有序的链表,默认是升序,也可以根据创建映射时所提供的Comparator进行排序,具体取决于使用的构造方法
最底层(Level 1)的链表包含所有元素
如果一个元素出现在Level i 的链表中,则它在Level i 之下的链表也都会出
每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素
查找、删除、添加
ConcurrentSkipListSet
内部采用ConcurrentSkipListMap实现
atomic
基本类型类
用于通过原子的方式更新基本类型
AtomicBoolean
原子更新布尔类型
AtomicInteger
原子更新整型
AtomicLong
原子更新长整型
数组
通过原子的方式更新数组里的某个元素
AtomicIntegerArray
原子更新整型数组里的元素
AtomicLongArray
原子更新长整型数组里的元素
AtomicReferenceArray
原子更新引用类型数组里的元素
引用类型
如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类
AtomicReference
原子更新引用类型
AtomicReferenceFieldUpdater
原子更新引用类型里的字段
AtomicMarkableReference
原子更新带有标记位的引用类型
字段类
如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类
AtomicIntegerFieldUpdater
原子更新整型的字段的更新器
AtomicLongFieldUpdater
原子更新长整型字段的更新器
AtomicStampedReference
原子更新带有版本号的引用类型
阻塞队列
ArrayBlockingQueue
一个由数组实现的FIFO有界阻塞队列
ArrayBlockingQueue有界且固定,在构造函数时确认大小,确认后不支持改变
在多线程环境下不保证“公平性”
实现
ReentrantLock
Condition
LinkedBlockingQueue
基于链接,无界的FIFO阻塞队列
PriorityBlockingQueue
支持优先级的无界阻塞队列
默认情况下元素采用自然顺序升序排序,可以通过指定Comparator来对元素进行排序
二叉堆
分类
最大堆
父节点的键值总是大于或等于任何一个子节点的键值
最小堆
父节点的键值总是小于或等于任何一个子节点的键值
添加操作则是不断“上冒”,而删除操作则是不断“下掉”
实现
ReentrantLock + Condition
二叉堆
DelayQueue
支持延时获取元素的无界阻塞队列
应用
缓存:清掉缓存中超时的缓存数据
任务超时处理
实现
ReentrantLock + Condition
根据Delay时间排序的优先级队列:PriorityQueue
Delayed接口
用来标记那些应该在给定延迟时间之后执行的对象
该接口要求实现它的实现类必须定义一个compareTo 方法,该方法提供与此接口的 getDelay 方法一致的排序。
SynchronousQueue
一个没有容量的阻塞队列
应用
交换工作,生产者的线程和消费者的线程同步以传递某些信息、事件或者任务
难搞懂,与Exchanger 有一拼
LinkedTransferQueue
链表组成的的无界阻塞队列
相当于ConcurrentLinkedQueue、SynchronousQueue (公平模式下)、无界的LinkedBlockingQueues等的超集
预占模式
有就直接拿走,没有就占着这个位置直到拿到或者超时或者中断
LinkedBlockingDeque
由链表组成的双向阻塞队列
容量可选,在初始化时可以设置容量防止其过度膨胀,如果不设置,默认容量大小为Integer.MAX_VALUE
运用
“工作窃取”模式
线程池
好处
降低资源消耗
通过重复利用已创建的线程降低线程创建和销毁造成的消耗
提高响应速度
当任务到达时,任务可以不需要等到线程创建就能立即执行
提高线程的可管理性
进行统一分配、调优和监控
Executor
Executors
静态工厂类,提供了Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 、Callable 等类的静态工厂方法
ThreadPoolExecutor
参数含义
corePoolSize
线程池中核心线程的数量
maximumPoolSize
线程池中允许的最大线程数
keepAliveTime
线程空闲的时间
unit
keepAliveTime的单位
workQueue
用来保存等待执行的任务的阻塞队列
使用的阻塞队列
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
threadFactory
用于设置创建线程的工厂
DefaultThreadFactory
handler
RejectedExecutionHandler,线程池的拒绝策略
分类
AbortPolicy:直接抛出异常,默认策略
CallerRunsPolicy:用调用者所在的线程来执行任务
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务
DiscardPolicy:直接丢弃任务
线程池分类
newFixedThreadPool
可重用固定线程数的线程池
分析
corePoolSize和maximumPoolSize一致
使用“无界”队列LinkedBlockingQueue
maximumPoolSize、keepAliveTime、RejectedExecutionHandler 无效
newCachedThreadPool
使用单个worker线程的Executor
分析
corePoolSize和maximumPoolSize被设置为1
使用LinkedBlockingQueue作为workerQueue
newSingleThreadExecutor
会根据需要创建新线程的线程池
分析
corePoolSize被设置为0
maximumPoolSize被设置为Integer.MAX_VALUE
SynchronousQueue作为WorkerQueue
如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程 ,可能会耗尽CPU和内存资源
任务提交
Executor.execute()
ExecutorService.submit()
任务执行
执行流程
线程池调优
两种模型
线程池监控
ScheduledThreadPoolExecutor
继承自ThreadPoolExecutor
给定的延迟之后运行任务,或者定期执行任务
内部使用DelayQueue来实现 ,会把调度的任务放入DelayQueue中。DelayQueue内部封装PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序
Future
异步计算
Future
提供操作
执行任务的取消
查询任务是否完成
获取任务的执行结果
FutureTask
实现RunnableFuture接口,既可以作为Runnable被执行,也可以作为Future得到Callable的返回值
内部基于AQS实现
Java技能秘籍
非技术因素
责任心
团队精神
主动性
性格
年龄
职业规划
工作业绩
技术攻关
应急处理
创新成果
知识分享
项目管理
程序开发案例
项目设计案例
网络
TCP/IP协议栈
HTTP协议
日志框架
传统框架
Common-logging
Log4j
JDK Logger
SLF4j
新框架
Logback
测试框架
传统框架
JUnit
EasyMock
TestNG
新框架
Mockito
开发工具
Eclipse
IntelliJ IDEA
VI编辑器
SVN版本控制
Git版本控制
Hotfix流程
Code Review机制
性能优化
系统级别优化
分层架构优化
JVM级别调优
代码级优化
前端性能优化
分库分表方案
资源调度优化
大数据和NoSQL
ZooKeeper
Hadoop生态
HBase
MongoDB
Cassandra
YARN资源调度
Java语言
异常处理机制
类继承体系
泛型编程
内部类应用
反射机制
序列化技术
Object类方法
字符串处理
引用类型(强/软/弱/虚)
集合框架
I/O流体系
JVM
多线程与并发
线程生命周期
并发工具包
GC机制
CMS收集器
并行收集器
G1收集器
内存分区
新生代(Eden/Survivor)
老年代
永久代
IO/NIO模型
同步阻塞IO
同步非阻塞IO
多路复用IO
异步IO
类加载机制
双亲委派模型
OSGI动态模块
中间件
存储引擎
MySQL
Oracle
DB2
缓存系统
Redis
Memcached
消息队列
JMS规范
Kafka(Partition/Stream)
RocketMQ
RabbitMQ
ActiveMQ
设计和架构
架构方法论
SOLID设计原则
UML建模
数据结构和算法
基础算法
二分查找
选择排序
冒泡排序
插入排序
快速排序
归并排序
堆排序
高级算法
Hash算法
分桶统计
大数据处理算法
分布式架构
负载均衡策略
水平伸缩方案
集群部署
一致性Hash算法
数据分片
分布式事务
分库分表实践
锁机制(乐观锁/悲观锁/行锁/分布式锁)
分区队列
SSH框架
Struts MVC框架
Spring生态(AOP/IOC/Spring MVC/Spring Boot/YAML配置)
Hibernate ORM
一致性协议
分布式协议(Paxos/ZAB/Raft)
消息传播协议(Gossip)
事务协议(2PC/3PC/4PC)
柔性事务(TCC)
CAP理论
BASE理论
java知识点总结
web
分支主题
tomcat
servlet
ajax
过滤
监听
前端
jsp
框架
Struts2
Hbernate
Spring
ssh
Spring Data
微信图片_20170905225219.png
基础二
面向对象
基本概念
封装
继承
多态
静态static
代码块
final
抽象类
接口
包及权限修饰符
微信图片_20170906153014.png
微信图片_20170906153021.png
内部类
集合/数组
数组
内存管理
集合
集合体系图.png
集合和数组的区别
API
被abstract修饰的类
被final修饰的
API汇总
jdk1.5特性
jdk1.7特性
接口类
IO流
多线程
方法加强
xml/schame
反射
网络编程
设计模式
子主题 1
子主题 2
基本语法
运算符
自增自减
访问控制
public private protected
循环 条件
if while for break continue
异常
反射
面向对象
类和对象
创建与销毁
equals()与hashCode()
枚举enum
enum与接口常量
集合类
List
ArrayList
LinkedList
Map
HashMap
LinkedHashMap
TreeMap
set
HashSet
LinkedHashSet
TreeSet
Queue
LinkedList
ArrayDeque
ConcurrentLinkedQueue/ConcurrentLinkedDeque
继承与多态
泛型
内部类
接口
zhujie
异常与错误
ERROR
Exception Throws
Collection类
List顺序容器
List接口
Iterable接口
Arraylist实现List接口
LinkedList实现List接口
Set(集合容器)
HashSet实现Set接口
TreeSet实现Set接口
EnumSet实现Set接口
数据类型
基本数据类型-值类型
引用类型
运算符
算术运算符
关系运算符
逻辑运算符
三目运算符
流程控制
顺序
选择
if - else if -else
switch
循环
while
do-while
for
break - continue
数组
具有相同数据类型的一组数据
获取数组的长度:length
Java面向对象
类和对象
方法的重载
this
封装
getter
setter
继承
单根性
方法的重写
super
抽象类
接口
多态
instanceof
Java高级
集合框架
Collection
List
ArrayList
LinkedList
Set
HashSet
Map
Key - Value
HashMap
Iterator
HasNext
Next
泛型
实用类
包装类
StringBuilder - StringBuffer
Random
Math
Date - Calendar
SimpleDateFormat
IO
File
根据方向
输入流
InputStream
FileInputStream
DataInputStream
ObjectInputStream
Reader
InputStreamReader
输出流
OutputStream
FileOutputStream
DataOutputStream
ObjectOutputStream
Writer
OutputStreamWriter
根据内容
字节流
InputStream
OutputStream
字符流
Reader
Writer
序列化
多线程
进程和线程
定义线程的2种方式
继承Thread
实现Runnable
线程的状态
优先级
常用方法
线程的同步
网络编程
TCP - UDP
XML
XML
Java操作XML
DOM
dom4j
xpath
反射
数据库
mysql
Orcle
jdbc
基础一
java语言基础
原始Java编译的过程
jdk,jvm,jre
doc常用命令
环境变量配置
java版本
eclipse
java语法格式
常量
关键字
注释
变量
数据类型
标识符
运算符
循环语句
方法
项目
黑马商城
功能过滤(没有登录有些功能不能使用)
邮箱激活
bos物流系统
主流软件系统类别简介
git
jQueryEasyUI
实现一键上传 及批量导入
WebService远程调用
易淆知识点
各种变量之间的关系
重载和重写
this和supper
设计模式
原则
创建模式
结构模式
行为模式
开源与类库
Apache Commons
Guava
I/O与网络
基本IO
文件IO
序列化
网络IO
socket
线程与并发
线程
锁
线程安全
原子操作
synchronized
同步与异步
共享与可见性
NIO
并发容器
ConcurrentHashMap
CopyOnWriteArrayList
Executor框架
线程池
newSingleThreadExecutor
newFixedThreadPool
newCachedTreadPool
newScheduledThreadPool
延迟与周期任务
ScheduledThreadPoolExecutor
Callable与Future
BlockingQueue
ArrayBlockingQueue
LinkedBlockQueue
DelayQueue
PriorityBlockingQueue
SynchronousQueue
并发
同步,异步,阻塞,非阻塞
Fork/Join
消息队列
ActiveMQ
IO设计模式
Reactor
Proactor
网络开发
数据结构
XML
DOM
SAX
Json
fastjson
gson
通信协议
HTTP/HTTPS
XMPP
socket
通信框架
MINA
netty
Javascript
数据类型
var
流程控制
常用函数
对话框
内置对象
数组
时间
Window
Dom操作
事件处理
this的用法
定时器
jQuery
选择器
基本选择器
层次选择器
过滤选择器
DOM操作
append
preppend
remove
empty
clone
事件处理
bind - unbind
事件参数
动画处理
show - hide - toggle
slideUp - slideDown - slideToggle
fadeIn - fadeOut - fadeToggle
animate
ajax
$.get
$.post
$.getJSON
$.ajax
$.load
Oracle
DDL
create
drop
alter
DML
insert
update
delete
数据查询
基本数据查询
子查询
联合查询
内连接
左外联
右外联
完全链接
DCL
grant
revoke
视图
存储过程
触发器
包
函数
字符串
日期
数学
jsp
9大内置对象
response、request、application、session、config、error、page
session - cookie - application
转发 - 重定向
JDBC
EL表达式
jstl
三层开发
javabean
MVC
servlet
controller
过滤器
监听器
服务器与容器
Tomcat
Apache
nginx
awt/swing
布局
事件
IDE
Eclipse
NetBeans
IntelliJ IDEA
测试与日志
JUnit
log4j
J2EE web
框架
基本MVC
spring体系
Struts
Hibernate
ibatis/mybatis
JPA
EJB
前端
javascript
jQuery
JSP/servlet
JDBC
缓存
Ehcache
编码与加解密
base64
MD5
对称加密
非对称加密
0 条评论
下一页