当前位置: 首页 > article >正文

[JAVAEE] 面试题(三) - Callable接口, ReentrantLock类, Semaphore信号量, CountDownLatch类

目录

一. Callable接口

1.1 Callable接口介绍

1.2 Callable接口 与 Runnable接口

1.3 Callable接口的使用

二. ReentrantLock类

2.1 lock() unlock() trylock()

2.2 synchronized关键字 与 ReentrantLock类的区别

三.  Semaphore信号量

3.1 PV操作

3.2 二元信号量

 四. CountDownLatch类

4.1 CountDownLatch类的使用

五. 总结


一. Callable接口

1.1 Callable接口介绍

Callable接口位于JUC(java.util.concurrent)包中. 被注释@FunctionalInterface, 表示Callable接口是函数式接口, Callable接口中有且仅有一个抽象方法.

1.2 Callable接口 与 Runnable接口

Runnable接口和Callable接口是并列关系, run方法与call方法并列.

Runnable中的run是任务的入口, 同理, Callable中的call也是任务的入口.

run没有返回值, call有返回值.

1.3 Callable接口的使用

Thread类没有提供构造方法来接受callable匿名类对象.

这时可以使用FutureTask对callable进行封装.

可以发现FutureTask类实现了RunnableFuture接口, RunnableFuture接口拓展了Runnable接口和Future接口. Thread类中提供了构造方法来接受FutureTask对象.FutureTask类中也提供了构造方法来接受Callable匿名对象.

所以, 综上所述, 可以通过FutureTask来找到Callable.

案例: 使用Callable创建线程, 并将原子类count中的值增加为50000.

private static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                for (int i = 0; i < 50000; i++) {
                    count.getAndIncrement();
                }
                return count.get();
            }
        };
//        Thread thread = new Thread(callable);
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();

        System.out.println(futureTask.get()); // get没有得到结果之前会一直阻塞, 获得结果为止.
    } 

 


二. ReentrantLock类

2.1 lock() unlock() trylock()

lock(): 加锁

unlock(): 解锁 (需要注意unlock不被调用的问题, 也就是线程提前终止)

trylock(): 不会阻塞, 加锁成功,返回true; 加锁失败, 返回false. 调用者根据返回值来决定下一步该做什么.

案例: 使用reentrantLock加锁, 来实现线程安全.

    static int count = 0;
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock(true);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                reentrantLock.lock();
                try {
                    count++;
                } finally {
                    reentrantLock.unlock();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                reentrantLock.lock();
                try {
                    count++;
                } finally {
                    reentrantLock.unlock();
                }
            }
        });

        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(count);
    }

2.2 synchronized关键字 与 ReentrantLock类的区别

 1. synchronized是关键字(是JVM内部通过C++实现的), ReentrantLock是JUC中的类.

 2. synchronized通过代码块来控制加锁和解锁, ReentrantLock通过lock, trylock, unlock来控制加锁和解锁.

 3. synchronized是非公平锁, ReentrantLock默认是非公平锁, 可以设置为公平锁.

 4. ReentrantLock除了提供lock, unlock外, 还提供了trylock. (不会阻塞, 加锁成功返回true, 加锁失败返回false, 调用者根据返回值来决定下一步做什么)

 5. ReentrantLock搭配的等待通知机制, 是Condition类, 比wait/notify功能更强大.


三.  Semaphore信号量

Semaphore(信号量), 描述了某种可用资源的个数.

3.1 PV操作

申请一个资源, 计数器就会 +1.  acquire

释放一个资源, 计数器就会 -1.   release

注意: 可用资源个数为0时, 再继续申请, 就会阻塞.

3.2 二元信号量

 可用资源个数为1, 申请资源相当于加锁, 释放资源相当于解锁.

(利用了可用资源个数为0时,  再继续申请, 就会阻塞).

    static int count = 0;
    public static void main(String[] args) throws InterruptedException{
        Semaphore semaphore = new Semaphore(1);
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                count++;
                semaphore.release();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                try {
                    semaphore.acquire(); // 可用资源个数为0时, 再继续申请, 阻塞.
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                count++;
                semaphore.release();
            }
        });


 四. CountDownLatch类

使用多线程, 经常将一个大任务拆分成多个小任务, 从而提升程序的运行效率.

4.1 CountDownLatch类的使用

1. 构造方法指定参数, 表示拆分成了几个小任务.

2. 每个小任务执行完毕后, 都调用一次countDown方法(当调用2次时, 说明小任务都执行完毕)

3. 主线程中调用 await 方法, 所有任务执行完后, 结束阻塞.

示例: 

 


五. 总结

 1. Callable接口和Runnable接口是并列关系, call方法与run方法都是任务入口, call有返回值, run没有返回值.

 2. 通过FutureTask类找到Callable匿名对象.(线程和任务解耦和).

 3. ReentrantLock类中的常用方法, lock, unlock, trylock.

 4. ReentrantLock类与synchronzied关键字的区别.

 5. Semaphore信号量, 描述某种可用资源的个数.

 6. 二元信号量, 模拟实现锁的效果.

 7. CountDownLatch类, 描述小任务的个数, 决定何时结束阻塞.


http://www.kler.cn/a/379170.html

相关文章:

  • C#属性 Property
  • 如何打开/关闭 GitLab 的版本检查功能?
  • Bartender 5 for Mac 菜单栏管理软件 安装教程【保姆级教程,操作简单小白轻松上手使用】
  • 深入理解跨域资源共享(CORS)安全问题原理及解决思路
  • 朴素贝叶斯分类器入门教程:使用Python进行文本分类
  • Linux中SPI
  • 在线教育辅助:SpringBoot试题库系统精讲
  • Android IPC机制(一)多进程模式
  • 每周算法比赛
  • qt 如何在本地进行打包
  • 【论文精读】LPT: Long-tailed prompt tuning for image classification
  • 读书笔记-《Spring技术内幕》(四)事务
  • 【亚马逊云】基于 AWS 使用CloudFormation快速部署 VMClarity 环境
  • celery在django项目中实现并发任务和定时任务
  • SOLIDWORKS 2025用户体验新功能
  • NineData云原生智能数据管理平台新功能发布|2024年10月版
  • distrobox install in ubuntu 22.04 / 在 ubuntu 22.04 上安装 distrobox (***) OK
  • qt的c++环境配置和c++基础【正点原子】嵌入式Qt5 C++开发视频
  • Stable Diffusion Web UI 1.9.4常用插件扩展-WD14-tagger
  • Spring Boot技术:校园社团信息管理的创新解决方案
  • 123.WEB渗透测试-信息收集-ARL(14)
  • 初始计算机网络
  • sqlserver、达梦、mysql的差异
  • React 组件生命周期与 Hooks 简明指南
  • HTTP代理是什么?有什么用?
  • git pull遇到一个问题