🔥个人主页: 中草药
一.Callable
在Java中,Callable
接口是一个可以返回结果的异步任务执行方式。它与Runnable
接口类似,都是描述一个"任务",但最主要的区别在于Callable
描述的是带返回值的任务,Runnable描述的是不带返回值的任务
Callable 通常需要搭配 FutureTask 来使⽤. FutureTask ⽤来保存 Callable 的返回结果. 因为Callable 往往是在另⼀个线程中执行的, 啥时候执行完并不确定.
FutureTask 就可以负责这个等待结果出来的⼯作.
想象去吃麻辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你⼀张 "小票" . 这个小票就是
FutureTask. 后面我们可以随时凭这张小票去查看自己的这份麻辣烫做出来了没.
接口定义
Callable
接口位于java.util.concurrent
包中,其定义如下:
java
public interface Callable<V> {
V call() throws Exception;
}
这里的关键点是call()
方法,它会抛出异常,这意味着你可以在call()
方法中处理任何需要的逻辑,并可能抛出异常。
使用Callable和Future
使用Callable
通常涉及到以下几个步骤:
-
创建Callable对象:
首先,你需要创建一个实现了
Callable
接口的类或者匿名内部类,并重写call()
方法。 -
获取FutureTask对象:
提交任务后,你会得到一个
Future
对象,该对象可以用来获取计算的结果或检查任务的状态。 -
从FutureTask获取结果:
使用
Future.get()
方法来获取计算的结果。需要注意的是,这将阻塞调用线程直到结果可用。
下面是一个简单的例子:
java
ublic static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable=new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 0; i < 1000; i++) {
sum+=i;
}
return sum;
}
};
FutureTask<Integer> futureTask=new FutureTask<>(callable);
Thread t=new Thread(futureTask);
t.start();
// 后续需要通过 FutureTask 拿到最终的结果.
System.out.println(futureTask.get());
}
注意事项
Callable
任务可能会抛出异常,这些异常需要被捕获处理。Future.get()
方法是阻塞的,如果不想阻塞主线程,可以使用Future.get(long timeout, TimeUnit unit)
来设置超时时间。
二.Semaphore
在Java中,Semaphore
是一个用于控制同时访问特定资源的线程数量的同步工具。它可以用来管理一组相关许可的集合。每个许可都代表对某个资源的一次访问权限。
构造函数
Semaphore(int permits)
:创建具有给定数量的许可证的Semaphore
。Semaphore(int permits, boolean fair)
:创建具有给定数量的许可证的Semaphore
,并指定是否公平地获取许可证。
主要方法
获取许可证
acquire()
:获取一个许可证,在没有可用许可证之前一直等待。acquireUninterruptibly()
:获取一个许可证,在没有可用许可证之前一直等待,即使线程被中断也不会立即返回。tryAcquire()
:尝试获取一个许可证,如果许可证可用则立即返回true,否则返回false。tryAcquire(long timeout, TimeUnit unit)
:尝试获取一个许可证,如果在给定的时间内许可证可用,则返回true,否则返回false。
释放许可证
release()
:释放一个许可证,返回一个许可证到池中。
查询状态
availablePermits()
:返回当前可用的许可证数量。drainPermits()
:获取所有可用的许可证,并返回获取的许可证数量。hasQueuedThreads()
:如果有线程正在等待获取许可证,则返回true。getQueueLength()
:返回正在等待获取许可证的线程数。
示例
java
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore=new Semaphore(3);
semaphore.acquire();
System.out.println("申请资源");
semaphore.acquire();
System.out.println("申请资源");
semaphore.acquire();
System.out.println("申请资源");
semaphore.acquire();
System.out.println("申请资源");
}
如上代码运行结果为
如运行结果所示,尽管申请了四次资源,但semaphore做了限制是3,因此只会显示三次打印
如若,测试以下代码
java
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore=new Semaphore(3);
semaphore.acquire();
System.out.println("申请资源");
semaphore.acquire();
System.out.println("申请资源");
semaphore.acquire();
System.out.println("申请资源");
semaphore.release();
System.out.println("释放资源");
semaphore.acquire();
System.out.println("申请资源");
}
结果为
注意事项
- 在使用
Semaphore
时,一定要确保在完成资源访问后调用release()
方法来释放许可证,否则会导致其他线程永远无法获取许可证。 - 如果不希望线程因等待许可证而被中断,可以使用
acquireUninterruptibly()
。 - 如果希望在等待获取许可证时能够响应中断信号,应使用
acquire()
方法,并在catch
块中处理InterruptedException
。 Semaphore
可以用于多种场景,比如控制并发请求的数量、限制数据库连接池的大小等。
三.CountDownlatch
CountDownLatch
是Java并发包java.util.concurrent
中的一个实用工具类,它允许一个或多个线程等待其他线程完成操作。简单来说,CountDownLatch
可以看作是一个计数器,当计数器的值减到0时,等待的线程就会被释放继续执行。
构造函数
CountDownLatch(int count)
:创建一个新的CountDownLatch
,初始化计数器为给定的值。
主要方法
-
减少计数器
void countDown()
:将计数器的值减1。当计数器的值变为0时,所有等待的线程都会被释放。
-
等待计数器变为0
-
void await()
:使当前线程等待,直到其他线程调用countDown()
方法使计数器的值减到0。 -
boolean await(long timeout, TimeUnit unit)
:使当前线程等待,直到其他线程调用countDown()
方法使计数器的值减到0,或者等待时间超过给定的超时时间。
-
-
查询计数器值
long getCount()
:返回当前计数器的值。
示例
下面是一个简单的使用CountDownLatch
的例子,结合线程池展示了如何使用CountDownLatch
来等待一些子线程完成它们的工作。
java
public static void main(String[] args) throws InterruptedException {
ExecutorService service= Executors.newFixedThreadPool(4);
// 构造方法的数字, 就是拆分出来的任务个数.
CountDownLatch countDownLatch=new CountDownLatch(20);
for (int i = 0; i < 20; i++) {
int id=i;
service.submit(()->{
System.out.println("任务"+id+"开始执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务"+id+"结束执行");
countDownLatch.countDown();
});
}
// 当 countDownLatch 收到了 20 个 "完成" , 所有的任务就都完成了.
// await => all wait
// await 这个词也是计算机术语. 在 python / js 意思是 async wait (异步等待)
countDownLatch.await();
System.out.println("所有任务已完成");
}
注意事项
- 当调用
await()
方法时,当前线程会等待,直到计数器的值变为0。如果当前线程在此期间被中断,那么它将抛出InterruptedException
,并且计数器的值不会改变。 - 如果你想设置一个等待的超时时间,可以使用
await(long timeout, TimeUnit unit)
方法。如果超时时间到了,线程将继续执行,但计数器的值仍然不变。 CountDownLatch
只能用于一次性事件。一旦计数器的值减为0,就不能再重置。如果需要多次使用,可以考虑使用CyclicBarrier
。
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐
制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸