视频来自: https://www.bilibili.com/video/BV1B7411L7tE?p=5&spm_id_from=pageDriver
看到一个新东西不会,狂神会先看源码
JUC = java.util.conrrunent
创建线程的三种方式
extends thread, implement runnable, implement callable接口
常规的实现并发的方式,用synchronized,以及
new Thread(new Runnable() {
@Override
public void run() {
String a = "run 1";
}
}).start();上面代码在java 8以后可用lambda表达式:
new Thread(() -> {
String a = "run 1";
}
}).start();锁的实现方式,Lock三部曲
1、 new ReentrantLock();
2. lock.lock(); //加锁
3. try { xxx} catch {} finaly { lock.unlock()}//解锁
池化技术: 事先准备好资源,有人要用,就来我这里拿,用完之后还给我
默认大小: 2
最小值、最大值
线程池的好处:
1. 降低资源消耗
2.提高响应的速度
3.(因为放到池子里了)方便管理
线程复用,控制最大并发数,管理线程
线程池: 三大方法,7种参数,4种拒绝策略
线程池三大方法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}线程池七大参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
按参数顺序依次对应:
1.核心线程数 线程池里面最小的活跃线程数
2.最大线程数 线程池里面最大的活跃线程数
3.保活时间 线程不工作的最大时间,超时了没有人调用就会释放
4.时间单位 超时用的时间单位
5.工作队列: 用来储存要执行,但是未执行的线程(也即为阻塞的线程)
6.线程工厂 用来创建线程的,一般不用动
7.拒绝策略
线程池的执行流程,任务来了先放入核心线程池里面,其次放入非核心线程,否则进入工作队列
Volitale 不保证原子性
public class VolatileNotAtomicTest {
private volatile static int num = 0;
public static void add() {
++num;// ++num和num++都是非原子操作,一行对应3-4行汇编指令
}
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for(int j= 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(num); // num 一般小于 20000,因为没有原子性
}
}保证有序性的原理是指令重排序,保证可见性的原理是MESE一致性缓存协议,以及CPU嗅探机制
深入理解CAS
Unsafe类: 因为java无法操作内存,而c++可以,Unsafe是Java的后门,通过这个类,调用native方法,来操作内存
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndAddInt(Object this, long valueOffset, int increment) {
int previousValue ;
do {
previousValue = this.getIntVolatile(this, valueOffset);
} while(!this.compareAndSwapInt(this, valueOffset, previousValue, previousValue + increment));
// 如果当前对象内存的值(this + valueOffset) 还是和我期望的值 previousValue相等
// 那么将该对象内存值改为 previousValue + increment
// compareAndSwapInt 是CPU原语言,操作很快
return var5;
}上述自旋操作,
池的最大线程数到底该如何定义
CPU 密集型: 几核,就是几, 可以保持CPI的效率最高,如下图,CPU核 = 逻辑处理器数量,即12个

如何获取核数:
System.out.println(Runtime.getRuntime().availableProcessors());
IO 密集型: 判断你程序中十分耗IO的线程,有多少个,比如有15个任务比较耗费线程,那么就设置最大线程数为两倍,30个
四大函数式接口
标明了@FunctionalInterface的接口
"lambda表达式就是为了优化匿名内部类而生"
Consumer, Function, Predicate, Supplier
只要是函数式接口,就能用lambda表达式简化
BlockingQueue四组API
add, remove element(返回队首)会抛出异常
offer,poll, peek(返回队首) 不会抛出异常
put,take 一直阻塞,string.sout
offer("element", 2, Timeunit.SECOND),poll(2, Timeunit.SECOND) 超时(2S)退出
ArrayBlockingQueue 基于数组结构的队列
LinkedBlockingQueue 基于链表结构的队列