尚硅谷 周阳老师 JUC 课程, 课程笔记
Future
定义操作异步任务执行的一些方法 , 专门处理耗时任务.
// Future 接口的定义
package java.util.concurrent;
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
三个特点
- 多线程
- 有返回
- 异步任务
对比
// 无返回值 无异常抛出
class MyThread implements Runnable {
@Override
public void run() {
}
}
// 有返回值 有异常抛出
class MyThread2 implements Callable<Object> {
@Override
public Object call() throws Exception {
return null;
}
}
// 有返回值 无异常抛出
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
}
使用
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Object> task = new FutureTask<>(new MyThread2());
new Thread(task).start();
System.out.println(task.get());
}
}
class MyThread2 implements Callable<Object> {
@Override
public Object call() throws Exception {
System.out.println("come in Callable...");
return "hello Callable";
}
}
/**
* 输出结果
* come in Callable...
* hello Callable
*/
优缺点
优点
Future + 线程池异步多线程任务配合, 显著提高执行程序的执行效率
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(3);
FutureTask<Object> task1 = new FutureTask<Object>(() -> {
Thread.sleep(300);
return "task1, over";
});
service.submit(task1);
FutureTask<Object> task2 = new FutureTask<Object>(() -> {
Thread.sleep(300);
return "task2, over";
});
service.submit(task2);
FutureTask<Object> task3 = new FutureTask<>(() -> {
Thread.sleep(200);
return "task3, over";
});
service.submit(task3);
service.shutdown();
System.out.println(task1.get());
System.out.println(task2.get());
System.out.println(task3.get());
System.out.println("main end.....");
}
缺点
- get() 阻塞, 必须等到结果才离开, 容易程序堵塞
- 一般放在最后
- 可以设置等待时间, 超过等待时间抛出超时异常.
- isDone() 轮询, 耗费无谓的CUP
CompletableFuture
减少阻塞版本的 Future, Future 的增强版
package java.util.concurrent;
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
...
}
创建
使用四个方法进行创建对象
// 1. 无返回值, 无线程池
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
// 2. 无返回值, 有线程池
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
// 3. 有返回值, 无线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
// 4. 有返回值, 有线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
线程池的创建
// 核心线程数
int corePoolSize = 5;
// 最大线程数
int maximumPoolSize = 15;
// 线程最大存活时间
int keepAliveTime = 30;
// 时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 任务队列
BlockingQueue<Runnable> workQueue =
new ArrayBlockingQueue<Runnable>(5);
// 线程工厂 -> MyThreadFactory <- 自定义线程工厂
ThreadFactory threadFactory =
new MyThreadFactory("MyThreadPool");
// 拒绝策略
RejectedExecutionHandler handler =
new ThreadPoolExecutor.DiscardOldestPolicy();
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 线程工厂的具体实现
public class MyThreadFactory implements ThreadFactory {
// 前缀
private final String prefix;
// 线程计数
private final AtomicInteger number = new AtomicInteger(1);
// 前缀赋值
public MyThreadFactory(String prefix) {
this.prefix = prefix;
}
// 线程工厂方法, 继承于 ThreadFactory, 重写
@Override
public Thread newThread(Runnable r) {
return new Thread(null, r, prefix + " " + number.getAndIncrement());
}
}
基本使用
public static void main(String[] args) {
// 核心线程数
int corePoolSize = 5;
// 最大线程数
int maximumPoolSize = 15;
// 线程最大存活时间
int keepAliveTime = 30;
// 时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 任务队列
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(5);
// 线程工程
ThreadFactory threadFactory = new MyThreadFactory("MyThreadPool");
// 拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
CompletableFuture.runAsync(() -> {
System.out.println("completableFuture.....NoReturn");
});
CompletableFuture.runAsync(() -> {
System.out.println("completableFuture.....NoReturn......Pool");
}, threadPoolExecutor);
CompletableFuture.supplyAsync(() -> {
System.out.println("completableFuture.....Return");
return "over, NoPool";
});
CompletableFuture.supplyAsync(() -> {
System.out.println("completableFuture.....Return.....Pool");
return "over, NoPool";
}, threadPoolExecutor);
CompletableFuture.supplyAsync(() -> {
System.out.println("... in come ....");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
int nextInt = ThreadLocalRandom.current().nextInt(10);
if(nextInt % 2 == 0){
throw new RuntimeException();
}
return nextInt;
}, threadPoolExecutor).whenComplete((i, e) -> {
if(e == null){
System.out.println("The number is : " + i);
}
}).exceptionally(e -> {
e.printStackTrace();
return 0;
});
threadPoolExecutor.shutdown();
}
优点
- 异步任务结束时会自动调用某个对象方法
- 主线程设置好回调之后, 不在关心异步任务的执行, 异步任务之间可以顺序执行
- 异步任务出错时, 会自动回调某个对象的方法
电商比价案例(Excuse me?)
package mall;
import pool.MyThreadPool;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* describe
* <p>
* 2022/7/17 16:57
*
* @author MoQuan
*/
public class MallDemo {
static List<Mall> mallList = Arrays.asList(
new Mall("JingDong"),
new Mall("DangDang"),
new Mall("TaoBao"),
new Mall("TianMao")
);
public static List<String> comparePrice() {
ThreadPoolExecutor pool = MyThreadPool.getPool();
try {
return mallList.stream()
.map(mall -> CompletableFuture.supplyAsync(
() -> String.format("<< MySQL >> in %s price is %.2f", mall.getName(), mall.getPrice())
, pool))
.collect(Collectors.toList())
.stream()
.map(future -> future.join())
.collect(Collectors.toList());
} catch (Exception e) {
e.printStackTrace();
} finally {
pool.shutdown();
}
return null;
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
List<String> prices = comparePrice();
long end = System.currentTimeMillis();
System.out.println("runtime : " + (end - start));
if (prices != null) {
for (String price : prices) {
System.out.println(price);
}
}
}
}
class Mall {
private String name;
private double price;
public Mall(String name) {
this.name = name;
this.price = ThreadLocalRandom.current().nextDouble() * 10 + 100;
}
public String getName() {
return name;
}
public Mall setName(String name) {
this.name = name;
return this;
}
public double getPrice() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return price;
}
public Mall setPrice(double price) {
this.price = price;
return this;
}
}
常用API
// 传入两个参数 v, e -> v: 使用该方法的 CompletableFuture 对象的返回值, e: 异常对象
public CompletableFuture<T> whenComplete(
BiConsumer<? super T, ? super Throwable> action) {
return uniWhenCompleteStage(null, action);
}
// 传入一个参数 e -> e: 异常对象, 当出现异常时进入该方法
public CompletableFuture<T> exceptionally(
Function<Throwable, ? extends T> fn) {
return uniExceptionallyStage(fn);
}
// thenRun 与上一个 CompletableFuture 对象不再有承接关系, 无传入无传出
// 默认
public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}
// 新开异步任务, 不再使用上一个 CompletableFuture 对象使用的线程池(重新选择线程池)
public CompletableFuture<Void> thenRunAsync(Runnable action) {
return uniRunStage(asyncPool, action);
}
// 新开异步任务, 使用传入的线程池 Executor executor
public CompletableFuture<Void> thenRunAsync(Runnable action,
Executor executor) {
return uniRunStage(screenExecutor(executor), action);
}
// thenAccept 与上一个 CompletableFuture 具有消费关系, 消费上一个的传出, 有传入无传出
// 默认
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
// 新开异步任务, 重新选择线程池
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
return uniAcceptStage(asyncPool, action);
}
// 新开异步任务, 使用传入线程池 Executor executor
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,
Executor executor) {
return uniAcceptStage(screenExecutor(executor), action);
}
// handle 与上一个 CompletableFuture 具有承接关系, 获取上一个的传出与异常对象, 有传入有传出
// 默认
public <U> CompletableFuture<U> handle(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(null, fn);
}
// 新开异步任务, 重新选择线程池
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(asyncPool, fn);
}
// 新开异步任务, 使用传入线程池 Executor executor
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
return uniHandleStage(screenExecutor(executor), fn);
}
// 与调用该方法的 CompletableFuture 的结果和当前方法中 CompletableFuture 的结果进行处理
public <U,V> CompletableFuture<V> thenCombine(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn) {
return biApplyStage(null, other, fn);
}
// 与调用该方法的 CompletableFuture 的处理速度和当前方法中 CompletableFuture 的处理速度进行比较, 使用最先完成的那个结果
public <U> CompletableFuture<U> applyToEither(
CompletionStage<? extends T> other, Function<? super T, U> fn) {
return orApplyStage(null, other, fn);
}
public <U> CompletableFuture<U> applyToEitherAsync(
CompletionStage<? extends T> other, Function<? super T, U> fn) {
return orApplyStage(asyncPool, other, fn);
}
public <U> CompletableFuture<U> applyToEitherAsync(
CompletionStage<? extends T> other, Function<? super T, U> fn,
Executor executor) {
return orApplyStage(screenExecutor(executor), other, fn);
}
锁
乐观锁与悲观锁
乐观锁
认为使用数据时不会有别的线程修改数据或资源, 不会添加锁.
使用判断规则来判断这次修改能否进行更新
判断规则
- 版本号机制
- CAS 算法, Java 原子类中的递增操作就通过 CAS 自旋实现
悲观锁
使用数据的时候, 必定加锁
synchronized
作用于 实例方法 -> 对当前对象加锁
作用于 代码块 -> 对括号里面配置的对象加锁
作用于 静态方法 -> 当前类加锁
识别
同步代码块中 -> 底层使用 monitorenter 和 monitorexit 指令 进行实现
实例方法中 -> 底层使用 ACC_SYNCHRONIZED 标识进行区分
静态方法中 -> 底层使用 ACC_STATIC 与 ACC_SYNCHRONIZED 标识进行区分
公平锁与非公平锁
公平锁 -> 先到先得
非公平锁 -> 顺序不定
为什么使用非公平锁?
恢复挂起的线程到真正锁的获取还是有时间差的, 从开发人员来看这个时间微乎其微, 以cup的角度去看时间差还是很明显的, 因此非公平锁能够更充分的使用 CPU 的时间片, 减少 CPU 空闲状态时间
多线程很重要的考量点是线程切换的开销, 采用非公平锁时, 一个线程请求所获取同步状态, 然后释放同步状态, 所以刚释放锁的线程在此刻再次获取同步状态的概率非常大, 所以就减少了线程的开销
可重入锁(递归锁)
同一个线程在外层方法获取锁的时候, 在进入该线程的内层方法会自动获取锁 ReentrantLock 与 synchronized 都是可重入锁
原理
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
当执行 monitorenter 时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1。否则需要等待,直至持有线程释放该锁。
当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
// _owner 指向持有 ObjectMonitor 对象的线程
// _WaitSet 存放处于 wait 状态的线程队列
// _EntryList 存放处于等待锁 block 状态的线程队列
// _recursions 锁的重入次数
// _count 记录该线程获取锁的次数
LockSupport 与 线程中断
线程中断
线程应该由自己决定是否停止, 但是某些情况下需要将比较耗时的线程进行停止 -> 中断
线程中存在一个中断标识, 若其他线程希望中断该线程则会调用该线程的 interrupt 方法将中断标识设置为 true, 但是检测到中断标识后如何处理需要该线程自己实现
相关 API
- interrupt
- interrupted
- isInterrupted
// 1. interrupt 中断次线程
// 2. interrupted 测试当前线程是否已被中断
// 3. isInterrupted 测试此线程是否已被中断
// 1. interrupt 中断次线程 -> 仅仅将线程的中断状态设置为 true
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
// 2. interrupted 测试当前线程是否已被中断 -> 静态方法
// -> 返回当前线程的中断状态, 并将中断状态设置为 false (重置状态)
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 3. isInterrupted 测试此线程是否已被中断 -> 判断当前线程是否被中断
public boolean isInterrupted() {
return isInterrupted(false);
}
// interrupted 与 isInterrupted 底层, 一个传递 true 一个 传递 false
private native boolean isInterrupted(boolean ClearInterrupted);
interrupt()
具体来说,当对一个线程,调用interrupt()时:
①如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。
②如果线程处于被阻塞状态(例如处于sleep, wait, join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
解决: 在 sleep 等方法的 try cache 中进行再次中断设置
③如果线程已经停止不会产生任何影响
LockSupport
用来创建锁和其他同步线程的基本线程阻塞原语
LockSupport 中的 park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程
线程的阻塞与唤醒
三种阻塞唤醒方法
Object 中的 wait() 和 notify()
Condition 的 await() 和 signal()
LockSupport 的 park() 和 unpark()
wait() 与 notify()
public static void main(String[] args) {
Object oLock = new Object();
new Thread(()->{
synchronized (oLock){
try {
System.out.println("coming!");
oLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start(