Java获得多线程的返回结果方式

一:Java创建线程方式

继承Thread类、实现Runnable接口或者实现 Executor接口
但是Runnable 的 run() 方法是不带返回值的,那如果我们需要一个耗时任务在执行完之后给予返回值,应该怎么做呢?

第一种方法:在 Runnable 的实现类中设置一个变量 V,在 run 方法中将其改变为我们期待的结果,然后通过一个 getV() 方法将这个变量返回。

package com.test.thread;

import java.util.*;
import sun.swing.AccumulativeRunnable;
//获得线程的返回结果方式一
/*
 *在runnable实现类中设置一个变量x,在run方法中将其改变为我们期待的结果,然后通过一个getX()方法将这个变量返回 
 */
public class RunnableTest {
		public static void main(String[] args) throws Exception {
			System.out.println("使用Runnable获取返回结果");
			List<Thread> workers = new ArrayList<>(10);
			List<RunnableAcction> tasks = new ArrayList<>(10);
			//创建10个线程,每个线程分别负责累加1-10,11-20,.........,91-100
			for(int i=0;i<10;i++) {
				RunnableAcction task = new RunnableAcction(i*10+1,(i+1)*10);
				Thread work = new Thread(task,"累加器线程"+i);
				workers.add(work);
				tasks.add(task);
				work.start();
			}
			int total = 0;
			for(int i = 0;i<workers.size();i++) {
				workers.get(i).join();
				total += tasks.get(i).getResult();
			}
			System.out.println("\n累加的结果:"+total);
		}
		
		static final class RunnableAcction implements Runnable{
			
			private  int a;
			public RunnableAcction(int a, int b) {
				super();
				this.a = a;
				this.b = b;
			}
			private  int b;
			private int result; 
			@Override
			public void run() {
				result = 0;
				try {
					for(int i=a;i<= b;i++) {
						result += i;
						Thread.sleep(100);
					}
				} catch (Exception e) {
					// TODO: handle exception
				}
				System.out.printf("(%s) - 运行结束,结果为 %d\n",Thread.currentThread().getName(),result);
			}
		     public int getResult() {//获取线程返回结果
		            return result;
		     }
		}
}

第二种方法:使用 Callable 和 FutureTask。

使用 FutureTask 的过程如下:
(1)通过一个 Callable 任务或者一个 Runnable(一开始就指定 result)任务构造 FutureTask;
(2)将 FutureTask 交给 Thread 去运行;
(3)使用 FutureTask 的 get 方法(或者 Thread 的 join 方法)阻塞当前线程直到获得任务的结果。

import java.util.*;
import java.util.concurrent.*;

public class CallableTest {

    public static void main(String[] args) throws Exception {
        System.out.println("使用 Callable 获得返回结果:");
        
        List<FutureTask<Integer>> futureTasks = new ArrayList<>(10);
        // 新建 10 个线程,每个线程分别负责累加 1~10, 11~20, ..., 91~100
        for (int i = 0; i < 10; i++) {
            AccumCallable task = new AccumCallable(i * 10 + 1, (i + 1) * 10);
            FutureTask<Integer> futureTask = new FutureTask<>(task);
            futureTasks.add(futureTask);
            Thread worker = new Thread(futureTask, "慢速累加器线程" + i);
            worker.start();
        }

        int total = 0;
        for (FutureTask<Integer> futureTask : futureTasks) {
            total += futureTask.get(); // get() 方法会阻塞直到获得结果
        }
        System.out.println("累加的结果: " + total);
    }

    static final class AccumCallable implements Callable<Integer> {

        private final int begin;
        private final int end;

        public AccumCallable(int begin, int end) {
            this.begin = begin;
            this.end = end;
        }

        @Override
        public Integer call() throws Exception {
            int result = 0;
            for (int i = begin; i <= end; i++) {
                result += i;
                Thread.sleep(100);
            }
            System.out.printf("(%s) - 运行结束,结果为 %d\n",
            Thread.currentThread().getName(), result);
            return result;
        }
    } 
}

二:FutureTask介绍

FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果。因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。

一个FutureTask 可以用来包装一个 Callable 或是一个runnable对象。因为FurtureTask实现了Runnable方法,所以一个 FutureTask可以提交(submit)给一个Excutor执行(excution)。

FutureTask执行多任务计算的使用场景:
利用FutureTask和ExecutorService,可以用多线程的方式提交计算任务,主线程继续执行其他任务,当主线程需要子线程的计算结果时,再异步获取子线程的执行结果。

相当于新起一个线程去执行,不影响主线程其他代码执行,只不过获取线程结果时需要阻塞等待

import java.util.*;
import java.util.concurrent.*;

public class FutureTest1 {

    public static void main(String[] args) {
    
        Task task = new Task();// 新建异步任务,然后执行futureTask
        FutureTask<Integer> future = new FutureTask<Integer>(task) {
            // 异步任务执行完成,回调
            @Override
            protected void done() {
                try {
                    System.out.println("future.done():" + get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        // 创建线程池(使用了预定义的配置)
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(future);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        // 可以取消异步任务
        // future.cancel(true);

        try {
            // 阻塞,等待异步任务执行完毕-获取异步任务的返回值
            System.out.println("future.get():" + future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    // 异步任务
    static class Task implements Callable<Integer> {
        // 返回异步任务的执行结果
        @Override
        public Integer call() throws Exception {
            int i = 0;
            for (; i < 10; i++) {
                try {
                    System.out.println("异步任务:"+Thread.currentThread().getName() + "_"                            + i);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return i;
        }
    }
}
package test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
 
public class TestFutureTask {
 
	public static void main(String[] args) {
		class Result {
			private String str;
		}
		ExecutorService executorService = Executors.newCachedThreadPool();
		final Result result = new Result();
		FutureTask<Result> futureTask = new FutureTask<Result>(new Runnable() {
			public void run() {
				try {
					System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
					Thread.sleep((long) (Math.random() * 10000));
					result.str = "sdsd";
					System.out.println("线程" + Thread.currentThread().getName() + "执行完成");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, result);
 
		try {
			System.out.println("准备开始执行子任务 ");
			executorService.execute(futureTask);
			Result r1 = futureTask.get();
			System.out.println("线程执行结果:" + r1.str);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		executorService.shutdown();
	}
}

执行结果如下:

准备开始执行子任务
线程pool-1-thread-1开始执行
线程pool-1-thread-1执行完成
线程执行结果:sdsd

项目中使用如下:

     /**
	 * 发HTML邮件公共方法
	 */
	public static void sendTextEmail(String address, String cc, String title, String content) {
		BPMEmailRecord record = setRecord(address, cc, title, content);
		try {
			Map<String, Object> data = getConfig();
			Properties ppt = (Properties) data.get("config");
			String from = (String) data.get("from");
			String account = (String) data.get("account");
			String password = (String) data.get("password");
			ExecutorService executorService= Executors.newSingleThreadExecutor();
	        FutureTask<Boolean> future=new FutureTask<>(()->
	        	{
                	Session session = null;
                	if (ppt != null) {
        				session = Session.getDefaultInstance(ppt);
        			} else {
        				session = Session.getDefaultInstance(props);
        			}
        			MimeMessage message = new MimeMessage(session);
        			// 设置发件人
        			if (StringUtils.isNotBlank(from)) {
        				message.setFrom(new InternetAddress(from));
        			} else {
        				message.setFrom(new InternetAddress(FROM));
        			}
        			// 设置收件人
        			if (address.contains(";") || address.contains(",")) {
        				message.setRecipients(RecipientType.TO, getAddress(address));
        			} else {
        				message.setRecipient(RecipientType.TO, new InternetAddress(address));
        			}
        			// 抄送
        			if (StringUtils.isNotBlank(cc)) {
        				if (cc.contains(";") || cc.contains(",")) {
        					message.setRecipients(RecipientType.CC, getAddress(cc));
        				} else {
        					message.setRecipient(RecipientType.CC, new InternetAddress(cc));
        				}
        			}
        			// 设置发送时间
        			message.setSentDate(new Date());
        			// 设置邮件主题
        			message.setSubject(title, "UTF-8");
        			// 设置纯文本内容为邮件正文
        			message.setContent(content, "text/html;charset=UTF-8");
        			// 保存并生成最终的邮件内容
        			message.saveChanges();
        			// 获得Transport实例对象
        			Transport transport = session.getTransport();
        			// 打开连接
        			if (StringUtils.isNotBlank(account) && StringUtils.isNotBlank(password)) {
        				transport.connect(account, password);
        			} else {
        				transport.connect(ACCOUNT, password);
        			}
        			// 将message对象传递给transport对象,将邮件发送出去
        			transport.sendMessage(message, message.getAllRecipients());
        			// 关闭连接
        			transport.close();
                    return true;
                }
	        );
	        executorService.execute(future);
	        try{
	            Boolean result = future.get(60, TimeUnit.SECONDS);
	            if (result && (record!=null)) {
	            	LOG.info("BPMEmailUtils发送邮件……" + "收件:" + address + ",抄送:" + cc==null?"":(",抄送:" + cc) + ",主题:" + title + ",内容:" + content);
					record.setSendTime(new Timestamp(System.currentTimeMillis()));
					record.setLog("success");
					saveOrUpdate(record);
				} else {
					LOG.error("BPMEmailUtils发送邮件……" + "创建日志失败,收件:" + address + ((cc==null)?"":(",抄送:" + cc)) + ",主题:" + title + ",内容:" + content);
				}
	        }
	        catch (TimeoutException e){
	        	record.setSendTime(new Timestamp(System.currentTimeMillis()));
				record.setLog("overtime");
				saveOrUpdate(record);
				LOG.error("BPMEmailUtils发送邮件……" + "发送超时,收件:" + address + ((cc==null)?"":(",抄送:" + cc)) + ",主题:" + title + ",内容:" + content);
//				future.cancel(true);
				return;
	        }
		} catch (Exception e) {
			e.printStackTrace();
			LOG.error("BPMEmailUtils发送邮件……" + "发送失败,收件:" + address + ((cc==null)?"":(",抄送:" + cc)) + ",主题:" + title + ",内容:" + content);
			record.setSendTime(new Timestamp(System.currentTimeMillis()));
			record.setLog(e.toString().substring(0, e.toString().length()>1000?1000:e.toString().length()));
		}
	}

参考文章:
https://blog.csdn.net/chenliguan/article/details/54345993
https://blog.csdn.net/linchunquan/article/details/22382487
https://segmentfault.com/a/1190000007767231

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值