JUC笔记

尚硅谷 周阳老师 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.....");

}
缺点
  1. get() 阻塞, 必须等到结果才离开, 容易程序堵塞
    1. 一般放在最后
    2. 可以设置等待时间, 超过等待时间抛出超时异常.
  2. 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();

}

优点

  1. 异步任务结束时会自动调用某个对象方法
  2. 主线程设置好回调之后, 不在关心异步任务的执行, 异步任务之间可以顺序执行
  3. 异步任务出错时, 会自动回调某个对象的方法

电商比价案例(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);
}

乐观锁与悲观锁

乐观锁

认为使用数据时不会有别的线程修改数据或资源, 不会添加锁.

使用判断规则来判断这次修改能否进行更新

判断规则

  1. 版本号机制
  2. 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
  1. interrupt
  2. interrupted
  3. 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(
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值