后段基础_09 | 青训营笔记

105 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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 进程号

总结:

  1. 对象锁和死锁
  2. 公平锁和非公平锁
  3. 可重入锁
  4. 死锁

五.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

  1. 判断线程是否被中断
  2. 清除当前中断状态

isInterrupt

判断是否被中断

LockSupport

用于创建锁和其他同步类的基本线程阻塞原语

线程等待唤醒机制

方式:

  1. Objext类中的wait和notify方法唤醒线程
  1. JUC中Condtion的await和signal方法
  1. LockSupport类阻塞和唤醒指定被阻塞的线程