1

前言

之前用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;
    }
}

十三
39 声望11 粉丝