Java多线程之FutureTask:带有返回值的函数定义和调⽤⽅式
这篇⽂章主要介绍了Java多线程之FutureTask:带有返回值的函数定义和调⽤⽅式,具有很好的参考价值,希望对⼤家有所帮助。如有错
误或未考虑完全的地⽅,望不吝赐教
FutureTask返回值的函数定义和调⽤
使⽤Runnable接⼝定义的任务是没有返回值的。很多时候,我们是有返回值的,为了解决这个问题,Java提供了Callable接⼝,可以返回
指定类型的值。
但是这个接⼝本⾝是不具备执⾏能⼒的,所以Java中,还有⼀个FutureTask类⽤于使⽤Callable接⼝定义带有返回值的任务。
使⽤⽰例
以下代码演⽰了定义和调⽤的整个过程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
le;
Task;
publicclassFutureTaskDemo{
publicstaticvoidtest2()throwsExecution{
//基于Lambda的Callable接⼝,在newFutureTask中的Lambda表达式即是Callable接⼝的实现
FutureTask
intt=0;
for(inti=0;i<10;i++)
t+=i;
returnt;
});
//使⽤Thread类执⾏task
n("Startcalling.");
longt1=me();
newThread(task).start();
longresult=();
longt2=me();
n("Finishcalling.");
("Result:%d,Time:%.3fms.n",result,(t2-t1)/1000000f);
}
}
执⾏后的输出:
Startcalling.
Finishcalling.
Result:45,Time:13.620ms.
Java多线程FutureTask⽤法及解析
1FutureTask概念
FutureTask⼀个可取消的异步计算,FutureTask实现了Future的基本⽅法,提空startcancel操作,可以查询计算是否已经完成,并且
可以获取计算的结果。
结果只可以在计算完成之后获取,get⽅法会阻塞当计算没有完成的时候,⼀旦计算已经完成,那么计算就不能再次启动或是取消。
⼀个FutureTask可以⽤来包装⼀个Callable或是⼀个runnable对象。因为FurtureTask实现了Runnable⽅法,所以⼀个FutureTask可
以提交(submit)给⼀个Excutor执⾏(excution).
2FutureTask使⽤场景
FutureTask可⽤于异步获取执⾏结果或取消执⾏任务的场景。
通过传⼊Runnable或者Callable的任务给FutureTask,直接调⽤其run⽅法或者放⼊线程池执⾏,之后可以在外部通过FutureTask的get
⽅法异步获取执⾏结果,因此,FutureTask⾮常适合⽤于耗时的计算,主线程可以在完成⾃⼰的任务后,再去获取结果。
另外,FutureTask还可以确保即使调⽤了多次run⽅法,它都只会执⾏⼀次Runnable或者Callable任务,或者通过cancel取消
FutureTask的执⾏等。
2.1FutureTask执⾏多任务计算的使⽤场景
利⽤FutureTask和ExecutorService,可以⽤多线程的⽅式提交计算任务,主线程继续执⾏其他任务,当主线程需要⼦线程的计算结果
时,在异步获取⼦线程的执⾏结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
publicclassFutureTest1{
publicstaticvoidmain(String[]args){
Tasktask=newTask();//新建异步任务
FutureTask
//异步任务执⾏完成,回调
@Override
protectedvoiddone(){
try{
n("():"+get());
}catch(InterruptedExceptione){
tackTrace();
}catch(ExecutionExceptione){
tackTrace();
}
}
};
//创建线程池(使⽤了预定义的配置)
ExecutorServiceexecutor=hedThreadPool();
e(future);
try{
(1000);
}catch(InterruptedExceptione1){
tackTrace();
}
//可以取消异步任务
//(true);
try{
//阻塞,等待异步任务执⾏完毕-获取异步任务的返回值
n("():"+());
}catch(InterruptedExceptione){
tackTrace();
}catch(ExecutionExceptione){
tackTrace();
}
}
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//异步任务
staticclassTaskimplementsCallable
//返回异步任务的执⾏结果
@Override
publicIntegercall()throwsException{
inti=0;
for(;i<10;i++){
try{
n(tThread().getName()+"_"
+i);
(500);
}catch(InterruptedExceptione){
tackTrace();
}
}
returni;
}
}
}
2.2FutureTask在⾼并发环境下确保任务只执⾏⼀次
在很多⾼并发的环境下,往往我们只需要某些任务只执⾏⼀次。这种使⽤情景FutureTask的特性恰能胜任。
举⼀个例⼦,假设有⼀个带key的连接池,当key存在时,即直接返回key对应的对象;当key不存在时,则创建连接。
对于这样的应⽤场景,通常采⽤的⽅法为使⽤⼀个Map对象来存储key和连接池对应的对应关系,典型的代码如下⾯所⽰:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
privateMap
privateReentrantLocklock=newReentrantLock();
publicConnectiongetConnection(Stringkey){
try{
();
if(nsKey(key)){
(key);
}
el{
//创建Connection
Connectionconn=createConnection();
(key,conn);
returnconn;
}
}
finally{
();
}
}
21
22
23
24
//创建Connection(根据业务需求,⾃定义Connection)
privateConnectioncreateConnection(){
returnnull;
}
在上⾯的例⼦中,我们通过加锁确保⾼并发环境下的线程安全,也确保了connection只创建⼀次,然⽽确牺牲了性能。改⽤
ConcurrentHash的情况下,⼏乎可以避免加锁的操作,性能⼤⼤提⾼,但是在⾼并发的情况下有可能出现
Connection被创建多次的现象。
这时最需要解决的问题就是当key不存在时,创建Connection的动作能放在connectionPool之后执⾏,这正是FutureTask发挥作⽤的时
机,基于ConcurrentHashMap和FutureTask的改造代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
privateConcurrentHashMap
FutureTask
publicConnectiongetConnection(Stringkey)throwsException{
FutureTask
if(connectionTask!=null){
();
}
el{
Callable
@Override
publicConnectioncall()throwsException{
//TODOAuto-generatedmethodstub
returncreateConnection();
}
};
FutureTask
connectionTask=bnt(key,newTask);
if(connectionTask==null){
connectionTask=newTask;
();
}
();
}
}
//创建Connection(根据业务需求,⾃定义Connection)
privateConnectioncreateConnection(){
returnnull;
}
经过这样的改造,可以避免由于并发带来的多次创建连接及锁的出现。
3部分源码分析
3.1构造⽅法
1
2
3
4
publicFutureTask(Runnablerunnable,Vresult){
le=le(runnable,result);
=NEW;//ensurevisibilityofcallable
}
3.2cancel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//这个⽅法有⼀个参数是否中断running
publicbooleancancel(booleanmayInterruptIfRunning){
/**
*这个有点晕啊逻辑关系是
*等价与if(state!=new||!eAndSwapInt(this,stateOfft,NEW,mayInterruptIfRunning?INTERRUPTING:
CANCELLED))
*这个意思是如果state不是new那么就退出⽅法,这时的任务任务坑是已经完成了或是被取消了或是被中断了
*如果是state是new就设置state为中断状态或是取消状态
*
**/
if(!(state==NEW&&
eAndSwapInt(this,stateOfft,NEW,
mayInterruptIfRunning?INTERRUPTING:CANCELLED)))
returnfal;
try{//incacalltointerruptthrowxception
//如果是可中断那么就调⽤系统中断⽅法然后把状态设置成INTERRUPTED
if(mayInterruptIfRunning){
try{
Threadt=runner;
if(t!=null)
upt();
}finally{//finalstate
eredInt(this,stateOfft,INTERRUPTED);
}
}
}finally{
finishCompletion();
}
returntrue;
}
以上为个⼈经验,希望能给⼤家⼀个参考
本文发布于:2022-11-15 20:13:20,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/88/26452.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |