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

目录

[一. Callable接口](#一. Callable接口)

[1.1 Callable接口介绍](#1.1 Callable接口介绍)

[1.2 Callable接口 与 Runnable接口](#1.2 Callable接口 与 Runnable接口)

[1.3 Callable接口的使用](#1.3 Callable接口的使用)

[二. ReentrantLock类](#二. ReentrantLock类)

[2.1 lock() unlock() trylock()](#2.1 lock() unlock() trylock())

[2.2 synchronized关键字 与 ReentrantLock类的区别](#2.2 synchronized关键字 与 ReentrantLock类的区别)

[三. Semaphore信号量](#三. Semaphore信号量)

[3.1 PV操作](#3.1 PV操作)

[3.2 二元信号量](#3.2 二元信号量)

[四. CountDownLatch类](#四. CountDownLatch类)

[4.1 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.

java 复制代码
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加锁, 来实现线程安全.

java 复制代码
    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默认是非公平锁, 可以设置为公平锁.

  1. 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时, 再继续申请, 就会阻塞).

java 复制代码
    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. 构造方法指定参数, 表示拆分成了几个小任务.
  1. 每个小任务执行完毕后, 都调用一次countDown方法(当调用2次时, 说明小任务都执行完毕)
  1. 主线程中调用 await 方法, 所有任务执行完后, 结束阻塞.


示例:


五. 总结

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

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

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

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

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

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

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

相关推荐
jjjxxxhhh1234 分钟前
RSA加密解密代码
开发语言·c++
重生之后端学习4 分钟前
236. 二叉树的最近公共祖先
java·数据结构·算法·职场和发展·深度优先
Sun_gentle11 分钟前
java.lang.RuntimeException: Could not load wrapper properties from ‘C:\Users\
java·开发语言·安卓
笨蛋不要掉眼泪22 分钟前
Nacos配置中心详解:核心用法、动态刷新与经典面试题解析
java·数据库·后端
键盘鼓手苏苏22 分钟前
Flutter for OpenHarmony:git 纯 Dart 实现的 Git 操作库(在应用内实现版本控制) 深度解析与鸿蒙适配指南
开发语言·git·flutter·华为·rust·自动化·harmonyos
橙露23 分钟前
面向对象编程思想:Java 与 Python 的封装、继承与多态对比分析
java·开发语言·python
上海合宙LuatOS30 分钟前
LuatOS核心库API——【io】 io操作(扩展)
java·服务器·前端·网络·单片机·嵌入式硬件·物联网
ShineWinsu37 分钟前
对于C++:模版进阶的解析
开发语言·c++·面试·笔试·求职·进阶·模版
追随者永远是胜利者1 小时前
(LeetCode-Hot100)42. 接雨水
java·算法·leetcode·职场和发展·go
lifallen1 小时前
点分治 (Centroid Decomposition)
java·数据结构·算法