Java同步组件之Condition,FutureTask
Java同步组件概况
- CountDownLatch : 是闭锁,通过一个计数来保证线程是否一直阻塞
- Semaphore: 控制同一时间,并发线程数量
- CyclicBarrier:字面意思是回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。
- ReentrantLock:是一个重入锁,一个线程获得了锁之后仍然可以反复加锁,不会出现自己阻塞自己的情况。
- Condition:配合
ReentrantLock
,实现等待/通知模型 - FutureTask:FutureTask实现了接口Future,同Future一样,代表异步计算的结果。
Condition
Condition
是多线程之间协调通信的工具类,除了有AQS
,还有可能存在Condition
队列(不存在或者存在一个以上,即多个等待队列)
某个或某些线程等待某个
Condition
,只有当该条件具备(signal
或者signAll
方法被调用)时,这些等待线程才会被唤醒,从而重新争夺锁。
Condition
是同步器AbstractQueuedSynchronized
的内部类,因为Condition
的操作需要获取相关的锁,所以作为同步器的内部类比较合理。
一个 Condition 包含一个等待队列,Condition拥有首节点firstWaiter和尾节点lastWaiter。当前线程调用Condition.await()方法时,将会以当前线程构造节点,并将节点从尾部加入等待队列。
Condition
代码演示
package com.rumenz.task;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
public static void main(String[] args) {
ReentrantLock reentrantLock=new ReentrantLock();
Condition condition=reentrantLock.newCondition();
ExecutorService executorService = Executors.newCachedThreadPool();
AtomicInteger atomicInteger=new AtomicInteger(0);
executorService.execute(()->{
try{
reentrantLock.lock();
System.out.println("计算1====开始");
int i = atomicInteger.addAndGet(10);
System.out.println("计算1====结果"+i);
condition.await();
atomicInteger.incrementAndGet();
System.out.println("最后结果"+atomicInteger.get());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
});
executorService.execute(()->{
try{
reentrantLock.lock();
Thread.sleep(5000);
System.out.println("计算====2");
int i = atomicInteger.addAndGet(40);
System.out.println("计算2====结果"+i);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
});
executorService.shutdown();
}
}
//计算1====开始
//计算1====结果10
//计算====2
//计算2====结果50
//最后结果51
FutureTask
FutureTask
可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。
FutureTask
代码演示
package com.rumenz.task;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskExample {
public static void main(String[] args) throws Exception{
List<FutureTask<Integer>> futureTasks=new ArrayList<>();
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0; i <10; i++) {
FutureTask<Integer> futureTask=new FutureTask<>(()->{
int res=0;
for (int j = 0; j < 50; j++) {
res+=10;
}
return res;
});
futureTasks.add(futureTask);
pool.submit(futureTask);
}
System.out.println("所有任务都已经提交,主线程开始干活");
Thread.sleep(5000);//模拟主线程的任务
System.out.println("主进程任务完成,开始获取子线程任务结果");
int res=0;
for(FutureTask<Integer> task:futureTasks){
try{
res+=task.get();
}catch (Exception e){
e.printStackTrace();
}
}
pool.shutdown();
System.out.println("最终计算结果:"+res);
}
}
//所有任务都已经提交,主线程开始干活
//主进程任务完成,开始获取子线程任务结果
//最终计算结果:5000
关注微信公众号:【入门小站】,解锁更多知识点