编写多线程程序一般有三种方法,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。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
2016-03-03 系列文章--Node.js学习笔记系列
2016-03-03 系列文章--8天学通MongoDB
2015-03-03 解决----Word无法创建工作文件,请检查临时环境变量
2014-03-03 VC中Error spawning cl.exe错误的解决方法.
2014-03-03 C语言屏幕打印,再删除打印的内容