JAVA创建线程有三种方式:
1.继承thread类(因为java只能单继承,所以不推荐使用此方式)
2.实现runnable接口
3.实现callable接口
当执行无返回参数的线程时使用runnable比较合适,但是当想要获取线程执行的返回结果时runnable就不能满足需求了,所以java内置了Callable和Future。
demo如下:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class testMain {
public static void main(String[] args) {
Callable<String> callable=new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
//放入futuretask中
FutureTask<String> future=new FutureTask<>(callable);
FutureTask<String> future2=new FutureTask<>(callable);
//运行futuretask
new Thread(future).start();
new Thread(future2).start();
try {
//获取futuretask返回结果
String result=future.get();
System.out.println(result);
String result2=future2.get();
System.out.println(result2);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}复制代码
控制台输出结果:
Thread-0
Thread-1
复制代码
需要注意的是 future的get方法会阻塞线程直到Future返回运行结果。
例子如下:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class testMain {
public static void main(String[] args) {
Callable<String> callable=new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(10000);
if(Thread.currentThread().getName().equals("Thread-1"))Thread.sleep(10000);
return Thread.currentThread().getName();
}
};
//放入futuretask中
FutureTask<String> future=new FutureTask<>(callable);
FutureTask<String> future2=new FutureTask<>(callable);
//运行futuretask
new Thread(future,"Thread-1").start();
new Thread(future2,"Thread-0").start();
try {
//获取futuretask返回结果
System.out.println("futureStart---"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
String result=future.get();
System.out.println("futureEnd---"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
System.out.println(result);
System.out.println("future2Start---"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
String result2=future2.get();
System.out.println("future2End---"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
System.out.println(result2);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}复制代码
运行结果如下:
futureStart---10:23:55
futureEnd---10:24:15
Thread-1
future2Start---10:24:15
future2End---10:24:15
Thread-0复制代码
这里Thread-1的休眠时间是20秒,Thread-0休眠时间是10秒,当Thread-1运行结束后Thread-0立马就返回了结果。
Java线程池也封装了Future和callable的任务运行。代码如下:
//创建线程池
ExecutorService executorService=Executors.newCachedThreadPool();
Callable<String> callable1=new Callable<String>(){
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
Callable<String> callable2=new Callable<String>(){
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
Callable<String> callable3=new Callable<String>(){
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
Future<String> f1=executorService.submit(callable1);
Future<String> f2=executorService.submit(callable2);
Future<String> f3=executorService.submit(callable3);
try {
System.out.println("f1---"+f1.get());
System.out.println("f2---"+f2.get());
System.out.println("f3---"+f3.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}复制代码
运行结果:
f1---pool-1-thread-1
f2---pool-1-thread-2
f3---pool-1-thread-3复制代码
executorService.submit会创建一个FutureTask,将callable放入FutureTask在线程池中运行。
当线程池运行多个callable时想要获得返回结果可以使用for循环获取,但是这样显得很是死板麻烦,java内置了一个ExecutorCompletionService,可以把线程池运行的Future结果放入LinkedBlockingQueue中。
代码如下:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class testMain3 {
public static void main(String[] args) {
ExecutorService executorService=Executors.newCachedThreadPool();
Callable<String> callable1=new Callable<String>(){
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
Callable<String> callable2=new Callable<String>(){
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
Callable<String> callable3=new Callable<String>(){
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
};
ExecutorCompletionService<String> completionService=new ExecutorCompletionService<>(executorService);
completionService.submit(callable1);
completionService.submit(callable2);
completionService.submit(callable3);
try {
System.out.println("f1---"+completionService.take().get());
System.out.println("f2---"+completionService.take().get());
System.out.println("f3---"+completionService.take().get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
复制代码
运行结果:
f1---pool-1-thread-1
f2---pool-1-thread-3
f3---pool-1-thread-2
复制代码
completionService封装了poll和take,类似阻塞队列BlockingQueue的用途。
take从队列中获取首位元素,如果队列中没有元素take会阻塞线程直到future返回结果。
poll从队列中获取首位元素,没获取到会返回NullPointerException,可以设置阻塞等待时间。
completionService.poll(9, TimeUnit.SECONDS) //等待9秒复制代码
写到这里也就差不多了,感谢大家的阅读,如有不足之处请指出,谢谢。
参考资料:《java并发编程实战》