前言
之前用java mail发送邮件,都是分给每个邮件一个线程,在邮件发送成功后,由该子线程将mail的信息(发送成功的邮箱和未发送的邮箱)存储到数据库中。
现在需要处理一封邮件有上万收件人的情况,如果还按照之前每个mail一个线程,发送的效率太低了,因此需要将一封邮件分到多个线程中去执行,让每个子线程处理一部分收件人,但是子线程执行完成后更新mail的信息,会出现数据覆盖的情况。
如果每个子线程执行完后能将发送邮件的信息返回给主线程,那么我们就可以在所有子线程结束后再存储mail的信息了。
Java Callable
Runnable任务不返回任何值,如果你希望在任务完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口,Callable是一种具有类型参数的泛型,它的类型参数表示的是从方法call()中返回的值,并且必须使用ExecutorService.submit()方法调用它。
public class TaskWithResult implements Callable<String>{
@Override
public String call() throws Exception {
return "result";
}
}
public class TaskDemo{
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
TaskWithResult task = new TaskWithResult();
Future<String> future = exec.submit(task);
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/* Output:
result
*/
submit()方法会产生Future对象,你可以用isDone()来查询Future是否已经完成,任务完成时,可以用get()方法获取任务的返回值,如果任务没有完成,调用get()方法会阻塞主线程。
代码实现
在获取返回结果时,get()会阻塞主线程,为了使发送邮件的函数不被阻塞,我们需要新创建一个线程来运行发送邮件的子线程。
类Mailer为实现了Callable的发送邮件的具体实现的代码。Mail为邮件的实体。
public class MailerTask extends Thread {
private Mail mail;//邮件信息
private String sender;//发件人信息
//每个线程发送邮件的最大数量
private static final int mail_limit = 100;
public static void send(Mail mail, String sender){
MailerTask mailerTask = new MailerTask();
mailerTask.setMail(mail);
mailerTask.setSender(sender);
mailerTask.start();
}
@Override
public void run() {
ExecutorService exec = Executors.newFixedThreadPool(20);
// 去除重复的收件人
List<String> sendTos = Arrays.stream(mail.getSendTo().split(";")).distinct().collect(Collectors.toList());
// 存储发送失败的收件人
String sendTo = "";
// 存储发送成功的收件人
String sended = "";
// 记录发送邮件的次数
int sendTimes = 0;
Mailer.setSender(sender);
List<Future<List<String>>> futures = new ArrayList<>();
// 每100个收件人创建一个线程用来发送邮件
for (int i = 0; i <= sendTos.size()/mail_limit; i++ ){
List<String> subSendTos = sendTos.stream().skip(i*mail_limit).limit(mail_limit).collect(Collectors.toList());
String subSendTo = subSendTos.stream().collect(Collectors.joining(";"));
mail.setSendTo(subSendTo);
Mailer mailer = new Mailer();
mailer.setMail(mail);
Future<List<String>> result = exec.submit(mailer);
futures.add(result);
}
// 处理结果
for (Future<List<String>> future : futures){
try {
String subSendTo = future.get().get(0);
String subSended = future.get().get(1);
String subtimes = future.get().get(2);
if (subSendTo != "") {
sendTo = sendTo + subSendTo + ";";
}
if (subSended != "" && subSended != null) {
sended += subSended;
sended += ";";
}
sendTimes += Integer.valueOf(subtimes);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
exec.shutdown();
mail.setSendTo(sendTo);
mail.setSended(sended);
mail.setSendTimes(sendTimes);
mail.setFinishDate(sendTo.isEmpty() ? new Date() : null);
MailService mailService = (MailService) ServiceFactory.getSpringBean("mailService");
mailService.saveMail(mail);
}
public void setSender(String sender) {
this.sender = sender;
}
public void setMail(Mail mail) {
this.mail = mail;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。