编写多线程程序一般有三种方法,Thread,Runnable,Callable。
1. Runable
Runnable是个接口,使用很简单:
- 1. 实现该接口并重写run方法
- 2. 利用该类的对象创建线程
- 3. 线程启动时就会自动调用该对象的run方法
package com.callable.runnable;
public class RunnableImpl implements Runnable {
public RunnableImpl(String acceptStr) {
this.acceptStr = acceptStr;
private String acceptStr;
} catch (InterruptedException e) {
System.out.println("hello : " + this.acceptStr);
public static void main(String[] args) {
Runnable runnable = new RunnableImpl("my runable test!");
long beginTime = System.currentTimeMillis();
new Thread(runnable).start();
long endTime = System.currentTimeMillis();
System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
Runnable实现的是void run()方法,Callable实现的是 V call()方法,并且可以返回执行结果,其中Runnable可以提交给Thread来包装下,直接启动一个线程来执行,而Callable则一般都是提交给ExecuteService来执行。通常在开发中结合ExecutorService使用,将任务的提交与任务的执行解耦开,同时也能更好地利用Executor提供的各种特性
2. Callable
相对于继承Thread来创建线程方式,使用Runnable可以让你的实现类同时实现多个接口,而相对于Callable及Future,Runnable方法并不返回任务执行结果且不能抛出异常。
Callable的接口定义如下:
public interface Callable<V> {
V call() throws Exception;
Callable并不像Runnable那样通过Thread的start方法就能启动实现类的run方法,所以它通常利用ExecutorService的submit方法去启动call方法自执行任务,而ExecutorService的submit又返回一个Future类型的结果,因此Callable通常也与Future一起使用
ExecutorService pool = Executors.newCachedThreadPool();
Future<String> future = pool.submit(new Callable{
步骤1:创建实现Callable接口的类SomeCallable<Integer>(略);
步骤2:创建一个类对象:
Callable<Integer> oneCallable = new SomeCallable<Integer>();
步骤3:由Callable<Integer>创建一个FutureTask<Integer>对象:
FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);
注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。
步骤4:由FutureTask<Integer>创建一个Thread对象:
Thread oneThread = new Thread(oneTask);
步骤5:启动线程:
oneThread.start();
此处也可以使用Callable和Fiture实现调用:
public class CallableTest {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
CallableDemo calTask=new CallableDemo();
Future<Integer> future =es.submit(calTask);
System.out.println("主线程在执行其他任务");
System.out.println("future.get()-->"+future.get());
System.out.println("future.get()未获取到结果");
System.out.println("主线程在执行完成");
此处也可以使用Callable+FutureTask的方式实现调用:
public class CallableTest {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
CallableDemo calTask=new CallableDemo();
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
System.out.println("主线程在执行其他任务");
if(futureTask.get()!=null){
System.out.println("futureTask.get()-->"+futureTask.get());
System.out.println("futureTask.get()未获取到结果");
System.out.println("主线程在执行完成");
3. Thread
通过继承Thread类来创建一个线程:
步骤1:定义一个继承Thread类的子类:
class SomeThead extends Thraad
步骤2:构造子类的一个对象:
SomeThread oneThread = new SomeThread();
步骤3:启动线程:
oneThread.start();
完整版例程如下:
package com.clzhang.sample.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadByCallable implements Callable<Integer> {
System.out.println("当前线程名称是:" + Thread.currentThread().getName());
System.out.println("循环变量i的值:" + i);
public static void main(String[] args) {
ThreadByCallable rt = new ThreadByCallable();
FutureTask<Integer> task = new FutureTask<Integer>(rt);
new Thread(task, "有返回值的线程").start();
System.out.println("子线程的返回值:" + task.get());
至此,一个线程就创建完成了。
4. 线程池方法
步骤1:创建线程池:
ExecutorService pool = Executors.newCachedThreadPool();
步骤2:通过Runnable对象或Callable对象将任务提交给ExecutorService对象:
Future<Integer> submit(Callable<Integer> task);
注释:Future是一个接口,它的定义如下:
public interface Future<T>
V get(long timeout, TimeUnit unit) throws ...;
void cancle(boolean mayInterrupt);
至此,一个线程就创建完成了。
注释:线程池需调用shutdown();方法来关闭线程。
详细例程如下:
public class CallableTest {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
CallableDemo calTask=new CallableDemo();
Future<Integer> future =es.submit(calTask);
System.out.println("主线程在执行其他任务");
System.out.println("future.get()-->"+future.get());
System.out.println("future.get()未获取到结果");
System.out.println("主线程在执行完成");
5. Runnable和Callable的区别是:
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得。
(3)call方法可以抛出异常,run方法不可以。
(4)运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()会throws InterruptedException或者ExecutionException;如果线程已经取消,会跑出CancellationException。取消由cancel 方法来执行。isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型、并返回 null 作为底层任务的结果。
6. Future
Future保存异步计算的结果,可以在我们执行任务时去做其他工作,并提供了以下几个方法
* cancel(boolean mayInterruptIfRunning):试图取消执行的任务,参数为true时直接中断正在执行的任务,否则直到当前任务执行完成,成功取消后返回true,否则返回false
* isCancel():判断任务是否在正常执行完前被取消的,如果是则返回true
* isDone():判断任务是否已完成
* get():等待计算结果的返回,如果计算被取消了则抛出
* get(long timeout,TimeUtil unit):设定计算结果的返回时间,如果在规定时间内没有返回计算结果则抛出TimeOutException
使用Future的好处:
1. 获取任务的结果,判断任务是否完成,中断任务
1. Future的get方法很好的替代的了Thread.join或Thread,join(long millis)
2. Future的get方法可以判断程序代码(任务)的执行是否超时,如:
future.get(60,TimeUtil.SECOND);
}catch(TimeoutException timeout){
log4j.log("任务越野,将被取消!!");
7. FutureTask
FutureTask实现了RunnableFuture接口,提供了即可以使用Runnable来执行任务,又可以使用Future执行任务并取得结果的构造器,所以可以利用FutureTask去封装Runnable或Callable对象,之后再submit任务。
public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> {
FutureTask除了实现了Future接口外还实现了Runnable接口,因此FutureTask也可以直接提交给Executor执行。 当然也可以调用线程直接执行(FutureTask.run())。接下来我们根据FutureTask.run()的执行时机来分析其所处的3种状态:
(1)未启动,FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态,当创建一个FutureTask,而且没有执行FutureTask.run()方法前,这个FutureTask也处于
未启动状态。
(2)已启动,FutureTask.run()被执行的过程中,FutureTask处于
已启动状态。
(3)已完成,FutureTask.run()方法执行完正常结束,或者被取消或者抛出异常而结束,FutureTask都处于
完成状态.
8. Callable和Runable的转换
无论是Runnable接口的实现类还是Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行,ThreadPoolExecutor或ScheduledThreadPoolExecutor都实现了ExcutorService接口,而因此Callable需要和Executor框架中的ExcutorService结合使用,我们先看看ExecutorService提供的方法:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
第一个方法:submit提交一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
第二个方法:submit提交一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
第三个方法:submit提交一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。
因此我们只要创建好我们的线程对象(实现Callable接口或者Runnable接口),然后通过上面3个方法提交给线程池去执行即可。还有点要注意的是,除了我们自己实现Callable对象外,我们还可以使用工厂类Executors来把一个Runnable对象包装成Callable对象。Executors工厂类提供的方法如下:
public static Callable<Object> callable(Runnable task)
public static <T> Callable<T> callable(Runnable task, T result)
9. 多线程方式调用
利用FutureTask和ExecutorService,可以用多线程的方式提交计算任务,主线程继续执行其他任务,当主线程需要子线程的计算结果时,在异步获取子线程的执行结果。
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskForMultiCompute {
public static void main(String[] args) {
FutureTaskForMultiCompute inst=new FutureTaskForMultiCompute();
List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
FutureTask<Integer> ft = new FutureTask<Integer>(inst.new ComputeTask(i, ""+i));
System.out.println("所有计算任务提交完毕, 主线程接着干其他事情!");
for (FutureTask<Integer> ft : taskList) {
totalResult = totalResult + ft.get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
System.out.println("多任务计算后的总结果是:" + totalResult);
private class ComputeTask implements Callable<Integer> {
private Integer result = 0;
private String taskName = "";
public ComputeTask(Integer iniResult, String taskName){
this.taskName = taskName;
System.out.println("生成子线程计算任务: "+taskName);
public String getTaskName(){
public Integer call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("子线程计算任务: "+taskName+" 执行完成!");
10. Thread Join方法
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
如下代码:
public class JoinTest implements Runnable{
for (int k = 0; k < 5; k++) {
public static void main(String[] args) throws Exception {
Runnable r = new JoinTest();
Thread t = new Thread(r);
程序的输出结果是5吗?答案是:有可能。其实你很难遇到输出5的时候,通常情况下
都不是5。当然这也和机器有严重的关系。为什么呢?我的解释是当主线程 main方法执行System.out.println(a);这条语句时,
线程还没有真正开始运行,或许正在为它分配资源准备运行。因为为线程分配资源需要时间,而main方法执行完t.start()方法后继续往下执行System.out.println(a);,这个时候得到的结果是a还没有被 改变的值0 。怎样才能让输出结果为5!其实很简单,join() 方法提供了这种功能。join() 方法,它能够使调用该方法的线程在此之前执行完毕。
public static void main(String[] args) throws Exception {
Runnable r = new JoinTest();
Thread t = new Thread(r);
这个时候,程序输入结果始终为5。