JUC基础-0602

7.Callable接口

创建线程的四种方式:

  1. 继承Thread
  2. 实现Runnable
  3. 实现Callable
  4. 线程池方式

Runnable 缺少的一项功能是,当线程 终止时(run完成时),我们无法使线程返回结果。为了支持此功能, Java 中提供了 Callable 接口。

Callable 接口的特点如下(重点

  • 为了实现 Runnable,需要实现不返回任何内容的 run()方法,而对于 Callable,需要实现在完成时返回结果的 call()方法。
  • call()方法可以引发异常,而 run()则不能。
  • 为实现 Callable 而必须重写 call 方法
  • 不能直接替换 runnable,因为 Thread 类的构造方法根本没有 Callable

Runnable和Callable区别:

  • 返回值
  • 是否抛异常
  • 实现方法名:run/call

7.1 Callable的方法

  • **public boolean cancel(boolean mayInterrupt)😗*用于停止任务。 ==如果尚未启动,它将停止任务。如果已启动,则仅在 mayInterrupt 为 true

    时才会中断任务。==

  • public Object get()抛出 InterruptedException,ExecutionException: 用于获取任务的结果。

    ==如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果。 ==

  • public boolean isDone():如果任务完成,则返回 true,否则返回 false

    可以看到 Callable 和 Future 做两件事-Callable 与 Runnable 类似,因为它封 装了要在另一个线程上运行的任务,而 Future 用于存储从另一个线程获得的结 果。实际上,future 也可以与 Runnable 一起使用。

    要创建线程,需要 Runnable。为了获得结果,需要 future。

7.2 FutureTask

核心原理

  • 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些 作业交给 Future 对象在后台完成
  • 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态
  • 一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去 获取结果。
  • 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法
  • 一旦计算完成,就不能再重新开始或取消计算
  • get 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常
  • get 只计算一次,因此 get 方法放到最后

Thread并不能直接使用Callable进行构造:

所以这里使用FutureTask:他既实现了Runnable,又构造包含了Callable。

class MyThread3 implements Callable {

    @Override
    public Object call() throws Exception {
        return 1024;
    }
}


//  FutureTask

FutureTask<Integer> futureTask = new FutureTask<>(new MyThread3());

FutureTask<Integer> futureTask2 = new FutureTask<>(()->1024);

FutureTask原理:未来任务

开启单线程去完成一个耗时长的支线任务,不影响主线任务。最后将这些任务都汇总起来,只汇总一次。

FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
            System.out.println(Thread.currentThread().getName() +" come in callable");
            return  1024;
        });

        new Thread(futureTask2,"futureTask2").start();

        while (!futureTask2.isDone()){
            System.out.println(Thread.currentThread().getName()+" wait!");
        }

        System.out.println(futureTask2.get());

        System.out.println(Thread.currentThread().getName()+" over!");

7.3 小结(重点)

在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些 作业交给 Future 对象在后台完成, 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态

8 JUC辅助类

JUC 中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过 多时 Lock 锁的频繁操作。这三种辅助类为:

  • CountDownLatch: 减少计数
  • CyclicBarrier: 循环栅栏
  • Semaphore: 信号灯

8.1 减少计数CountDownLatch

  1. CountDownlatch类:1构造2方法
    1. 构造方法 : CountDownLatch (int count):设置初始值
    2. 等待方法:await( ):当初始值为0时,可以执行一段逻辑,当初始值不为零是
    3. 计数方法:countDown():可以让初始值每次减1
  2. CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行 减 1 的操作,使用 await 方法等待计数器不大于 0,然后继续执行 await 方法 之后的语句。
    • CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,这 些线程会阻塞
    • 其它线程调用 countDown 方法会将计数器减 1(调用 countDown 方法的线程 不会阻塞)
    • 当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行
  3. 场景: 6 个同学陆续离开教室后值班同学才可以关门。
//演示 CountDownLatch
public class CountDownLatchDemo {
    //6个同学陆续离开教室之后,班长锁门
    public static void main(String[] args) throws InterruptedException {

        //创建CountDownLatch对象,设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);

        //6个同学陆续离开教室之后
        for(int i=1;i<=6;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 号同学离开");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }

        //等待
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
    }
}

8.2 循环栅栏CyclicBarrier

  1. 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的
    程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环的 barrier。
  2. CyclicBarrier 看英文单词可以看出大概就是循环阻塞的意思,在使用中 CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一 次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后 的语句。可以将 CyclicBarrier 理解为加 1 操作
  3. 方法:
    1. 构造方法:CyclicBarrier (int parties) / CyclicBarrier (int parties, Runnable barrierAction)
    2. Await():
    3. getNumberWaiting:
    4. getParties:
    5. isBroken:
    6. Reset:
  4. 场景: 集齐 7 颗龙珠就可以召唤神龙
//集齐7颗龙珠就可以召唤神龙
public class CyclicBarrierDemo {

    //创建固定值
    private static final int NUMBER = 7;

    public static void main(String[] args) {
        //创建CyclicBarrier
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()->{
            System.out.println("收集7颗龙珠就可以召唤神龙");
        });


        //集齐七颗龙珠过程
        for(int i=1;i<=7;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 星龙珠被收集");
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}

注意:如果循环的次数不是7,大于或小于7,在打印完之后,线程不会停止。

8.3 信号灯Semaphore

  1. 一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。
    每个 release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
  2. Semaphore 的构造方法中传入的第一个参数是最大信号量(可以看成最大线 程池),每个信号量初始化为一个最多只能分发一个许可证。使用 acquire 方 法获得许可证,release 方法释放许可。
public class SemaphoreDemo {
    public static void main(String[] args) {
        //创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(3);

        //模拟6辆汽车
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                try {
                    //抢占
                    semaphore.acquire();

                    System.out.println(Thread.currentThread().getName()+" 抢到了车位");

                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                    System.out.println(Thread.currentThread().getName()+" ------离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}
//	执行结果:
1 抢到了车位
2 抢到了车位
3 抢到了车位
1 ------离开了车位
4 抢到了车位
4 ------离开了车位
5 抢到了车位
2 ------离开了车位
6 抢到了车位
5 ------离开了车位
3 ------离开了车位
6 ------离开了车位
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值