BlockingQueue、Future、CompletionService总结

线程

java中线程的状态

1、NEW(初始化状态):通过new Thread()创建一个线程。
2.、RUNNABLE(可运行状态+运行状态):调用run方法,则进入可运行状态;若获得了cpu使用权,则从可运行状态变为运行状态;yield会让线程放弃cpu使用权,从运行状态变为可运行状态。
3、 BLOCKED(阻塞状态):竞争没拿到锁则进入阻塞;通过notify唤醒的线程也进入阻塞状态;若竞争到资源,则进入到RUNNABLE。
4.、WAITING(无时限等待+有时限等待):调用sleep、wait、join、LockSupport.park则线程进入等待状态, 可通过超时、notify、LockSupport.unPark唤醒,使线程进入RUNNABLE。
5.、TERMINATED(终止状态):线程执行结束。

常用方法和类

1、sleep:当前线程从 Running 进入TIMED_WAITING状态,不会释放对象锁;支持中断(会清除中断标志)。
2、yield:当前线程从 Running 进入RunableE状态,不会释放对象锁。
3、interrupt(): 将线程的中断标志位设置为true,不会停止线程。
5、isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位。
6、Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle。

中断说明:相比于stop,不管线程有没有执行完成,强制地关闭;通过中断interrupt可以更加优雅的来实现。需要关闭业务时通过Thread.currentThread().interrupt()来对当前线程发起中断;在正常业务中通过Thread.currentThread().isInterrupted()来监测是否被中断,若中断则结束业务流程。

7、Runnable:run方法是没有返回值的。
8、Callable:call方法是有返回值的,通过Future可以拿到执行的结果。
9、ThreadPoolExecutor线程池提交任务方法。
9.1、execute:是没有返回值的。
9.2、submit:是有返回值的,通过Future可以拿到执行的结果。

BlockingQueue阻塞队列

常用方法

1、poll:获取元素,不阻塞,队列没元素时,返回空。
2、offer:插入元素,不阻塞,队列满时,返回false。
3、take :获取元素,获取并移除队列的头结点,通常在队列里有数据的时候是可以正常移除的。可是一旦执行 take 方法的时候,队列里无数据,则阻塞,直到队列里有数据。一旦队列里有数据了,就会立刻解除阻塞状态,并且取到数据。
4、put :插入元素,如果队列没有满,那就和普通的插入一样是正常的插入,但是如果队列已满,那么就无法继续插入,则阻塞,直到队列里有了空闲空间。如果后续队列有了空闲空间,比如消费者消费了一个元素,那么此时队列就会解除阻塞状态,并把需要添加的数据添加到队列中。

ArrayBlockingQueue

基于数组结构实现的一个有界阻塞队列。

1、数组结构,容量固定,不支持扩容
2、插入元素和获取元素使用的是同一个ReentrantLock锁,因此存元素时不能插入元素,存取互相排斥
3、条件队列notEmpty:没有元素获取时,调用await阻塞在该队列上;当插入元素时,调用signal唤醒阻塞的获取元素的线程。
4、条件队列notFull:插入元素队列满了没容量时,调用await阻塞在该队列上;当获取元素时,调用signal唤醒阻塞的插入元素的线程。

LinkedBlockingQueue

基于链表结构实现的一个无界阻塞队列,指定容量则是有界阻塞队列。

1、链表结构,可以指定容量,不指定默认无限大,通过Node节点存储元素
2、锁分离:存取使用的是两个ReentrantLock锁,获取元素使用takeLock,插入元素使用putLock,提高了队列的吞吐量
3、条件队列notEmpty=takeLock.newCondition():没有元素获取时,调用await阻塞在该队列上;当插入元素时,调用signal唤醒阻塞的获取元素的线程。
4、条件队列notFull=putLock.newCondition():插入元素队列满了没容量时,调用await阻塞在该队列上;当获取元素时,调用signal唤醒阻塞的插入元素的线程。

Future

通常通过FutureTask来使用,FutureTask实现了Future

常用方法

1、boolean isDone():任务是否已经完成。
2、get():阻塞等待任务执行结束,然后获得结果。

Future的缺陷

1、并发执行任务,只能通过get阻塞获取结果
2、无法组合多个任务,如果你运行了多个任务,并期望在它们全部执行结束后执行特定功能,在Future中是做不到的。

使用示例
public static void main(String[] args) throws ExecutionException, InterruptedException {
    Task task = new Task();
    FutureTask<Integer> futureTask = new FutureTask<>(task);
    new Thread(futureTask).start();
    System.out.println("task运行结果:" + futureTask.get());
}

static class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            sum += i;
        }
        return sum;
    }
}

CompletionService

主要功能就是边执行任务,边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞,可以实现先执行完的先取结果。
实现原理:通过阻塞队列和FutureTask来实现,内部有一个先进先出的阻塞队列,用于保存已经执行完成Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,通过Future的get方法获取最终的结果。并且将线程池Executor和阻塞队列BlockingQueue的功能融合在了一起,能够让批量异步任务的管理更简单。

常用构造方法

1、runAsync(Runnable runnable, Executor executor);无返回值
2、supplyAsync(Supplier< U> supplier, Executor executor):有返回值,会阻塞

优势

1、批量提交异步任务的时候建议使用CompletionService
2、能够让异步任务的执行结果有序化。先执行完的先进入阻塞队列
3、支持自己创建线程池
4、实现了对任务的编排能力,可以自行组织不同任务的运行顺序、规则以及方式。

使用示例
CompletableFuture<Void> task1 = CompletableFuture
    .runAsync(() -> {
        System.out.println("task1执行...");
        sleep(1, TimeUnit.SECONDS);
    });
CompletableFuture<String> task2 = CompletableFuture
    .supplyAsync(() -> {
        System.out.println("task2执行...");
        sleep(2, TimeUnit.SECONDS);
        return "task2数据";
    });
//任务3:任务1和任务2完成后执行
CompletableFuture<String> task3 = task1.thenCombine(task2, (__, tf) -> {
    System.out.println("task2的结果: " + tf);
    System.out.println("task1和task2执行完成后返回结果");
    return "task3的结果";
});
//等待任务3执行结果
System.out.println(task3.join());
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值