这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
一.概念
进程和线程
线程状态
并行
并发
管程
- Monitor监视器 -> 锁
只有持有监视器对象才能对资源进行访问
锁 -> 监视器对象
用户线程和守护线程
- 用户线程:用户自己创建的线程
- 守护线程:JVM自带的线程,如垃圾回收
如果都是守护线程,则JVM会结束
二.Future
Future理论和FuterTask异步任务
实现异步线程
- Runnable接口只能实现多线程,但不能实现由返回值和异步
- 通过RunnableFuture接口继承Runnable接口和Task接口并且通过依赖注入Callable接口或者Runnable接口的方式实现
- 通过isDown和get的轮询方式获取返回值
while (true){
if (futureTask1.isDone()){
System.out.println(futureTask1.get());
break;
} else {
TimeUnit.MILLISECONDS.sleep(500);
System.out.println("轮询");
}
}
缺点:
- get会阻塞
- 通过轮询方式获取会导致不释放
- Futer对结果的获取不是很友好
三.CompletableFuture
因为FuterTask的get方法会导致阻塞
四大构造方法
- 无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
- 有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
使用
不用通过Thread类来开启线程,直接使用该类
// 开启线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try {
// 创建对象
CompletableFuture.s upplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "----come in");
int result = 0;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("1秒后出结果:" + result);
return result;
}, threadPool).whenComplete((v, e) -> {
if (e == null) {
System.out.println("计算完成,更新" + v);
}
}).exceptionally(e -> {
e.printStackTrace();
System.out.println("异常:" + e);
return null;
});
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
System.out.println(Thread.currentThread().getName()+"线程忙别的任务");
常用API
获得结果触发计算
- get
- get(long timeout , TimeUnit uni)
- join
- getNow(String 兜底)
- complete(String 是否被打断)
对计算结果进行处理 Function
- thenApply:进行下一步操作
- whenComplete(value,exception):获取上面的值,和异常
- handle:有异常也可以继续往下走
对计算结果进行处理 Consumer,直接处理,没有返回值
- thenAccept
对计算速度进行选用
- applyToEither:谁先完成
对计算结果进行合并
- thenCombine
四.锁
悲观锁乐观锁
定义
- 悲观锁:直接加锁,保证只有一个线程可以访问数据
- 乐观锁:只有在修改数据的时候加锁
-
- 版本号
- CAS自旋
锁案例
- 案例1:顺序调用,顺序执行
- 案例2:顺序调用时出现等待,顺序执行
锁的是对象,只有唯一线程可以访问
- 案例3:非锁方法会先执行
普通方法和被锁方法是分开的
- 案例4:两个对象调用,分开执行了
锁的是对象,不会产生争抢
- 案例5:静态同步方法,顺序执行
- 案例6:两个对象,静态同步方法,顺序执行
锁的是类
- 案例7:一个静态同步方法,一个普通同步方法,谁快
- 案例8:两个对象,一个静态同步方法,一个普通同步方法,谁快
公平锁和非公平锁
定义:
- 公平锁:按照顺序执行,先到先得
- 非公平锁:抢锁
线程切换会有损耗,非公平锁效率可以叫高一点
可重入锁
定义:
获取锁的对象的内部继续获取同一把锁,为了防止递归的时候发生死锁
种类:
隐式:synchronize默认就是可重入锁
显式:Lock。手动指定
死锁
是什么:
两个或两个以上的线程争抢资源导致的互相等待的现象。
怎么发现死锁:
jps -l
jstack 进程号
总结:
- 对象锁和死锁
- 公平锁和非公平锁
- 可重入锁
- 死锁
五.LockSupport与线程中断
首先:线程应该由自己结束而不是被其他线程强行结束
其次:线程不能被停止。Java提供了停止线程的协商机制——中断。
interrupt只能给线程对象将中断表示设成true
线程中断
线程中断方法
- 设置一个volatile设置的变量
- 设置AtomicBoolean
- 通过Thread的中断API实现
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "/t isInterrupted 被修改为true");
break;
}
System.out.println("t1 ---hello");
}
}, "t1");
t1.start();
TimeUnit.SECONDS.sleep(3);
new Thread(()->{
t1.interrupt();
},"t2").start();
APi
interrupt
设置线程的中断为true,发起一个协商而不会立刻停止线程
- 调用底层interrupt0函数,
- 如果线程处于阻塞状态(sleep、wait、join)状态,线程会立刻退出被阻塞状态,并抛出一个异常
- 已经结束的线程不会受影响
静态方法interrupted
- 判断线程是否被中断
- 清除当前中断状态
isInterrupt
判断是否被中断
LockSupport
用于创建锁和其他同步类的基本线程阻塞原语
线程等待唤醒机制
方式:
- Objext类中的wait和notify方法唤醒线程
- JUC中Condtion的await和signal方法
- LockSupport类阻塞和唤醒指定被阻塞的线程