我们如何以循环方式使用多线程?

时间:2012-07-30 04:22:09

标签: java multithreading round-robin

我想以多线程方式阅读10封邮件帐户的未读邮件。

但是如果线程池大小为5,那么将从线程池中使用5个线程。每个线程将读取一个邮件帐户。因此,一旦Thread_1读取了第一个邮箱,它应该读取mailbox_6。然后线程2将读取mailbox_7。

当所有邮件帐户都被阅读一次后,该周期将从第一个邮件帐户开始。

我们怎样才能在java中做到这一点?

6 个答案:

答案 0 :(得分:4)

这应该很简单。您创建一个包含5个线程的固定线程池,然后将10个作业提交到池中 - 每个用户电子邮件帐户1个:

// create a thread pool with 5 workers
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// submit all 10 user email accounts to the pool to be processed in turn
for (UserEmail userEmail : userEmailsToProcess) {
    threadPool.submit(new EmailProcessor(userEmail));
}
// once we have submitted all jobs to the thread pool, it should be shutdown
threadPool.shutdown();
...
// here's our runnable class which does the actual work
public class EmailProcessor implements Runnable {
    private UserEmail userEmail;
    public MyJobProcessor(UserEmail userEmail) {
        this.userEmail = userEmail;
    }
    public void run() {
        // read the user email
        ...
    }
}

UserEmail类可以将电子邮件的文件名保存为“已读”或可能是帐户名称或其他内容。由您来决定如何表示要阅读的邮件帐户和邮件。

[[来自评论:]]

  我有10个邮箱,比如.. mailbox1 ... mailbox10,现在我有5个来自线程池的线程,所以thread1将获取任何邮箱,所以我们假设它会选择mailbox1,然后thread2将选择mailbox2,thread3将选择mailbox3, thread4将选择mailbox4而thread5将选择mailbox5,现在当thread1(具有已定义的特定时间段)将自由时,它应该是pickbox6 - mailbox10,任何尚未读取的人都不应该从mailbox1中选择任何邮箱 - mailbox5 ,直到所有尚未阅读的邮箱。

哦,我明白了。一种解决方案是让一个调度线程每隔一段时间都在睡觉和醒来,以查看邮箱是否有任何邮件。如果他们这样做,那么它将一个作业服务到线程池,以便读取该邮箱。一旦读取了邮箱,该线程就会返回并要求下一个邮箱进行处理。调度线程将继续将邮箱添加到线程池,直到它被告知停止。

如果EmailProcessor中有很多上下文,那么你可以拥有你的线程池,但是他们可以使用BlockingQueue<File>消息来告诉他们需要注意哪些邮箱。

答案 1 :(得分:1)

如何简单地创建任务的并发(或同步,在这种情况下无关紧要)集合(可能是队列)。每项任务都将从电子邮件帐户加载所需的数据。然后让每个线程从集合中获取任务,直到它为空。

每个线程都会引用此集合并对其进行循环。虽然集合不是空的,但从中获取任务并处理它。

答案 2 :(得分:1)

跟踪已阅读的电子邮件帐户。 例如,定义类似的东西,

//total number of email accounts that need to be read.
private int noOfEmails=10;

//the thread pool that is used to read the emails
private ExecutorService threadPool = Executors.newFixedThreadPool(5);

//the tracker array that keeps track of the emails that 
//are already read. This array is cleared only after all 10
//emails are read. 
private ArrayList<String> emailTracker=new ArrayList<String>(noOfEmails);

//any changes to emailTracker should be synchronized as 
//this is a common data shared between all 5 threads.
private Object syncObject=new Object();

在Runnable实现中,检查emailTracker是否包含您的电子邮件帐户标识符,如果是这样,表明它已被读取,请返回并等待emailTracker被清除。当读取所有10个电子邮件帐户时,它将被清除。

        if(emailTracker.contains(email.identifier))
        {
            return;
        }

        //read email.
        email.read();
        //simple synchronization.
        synchronized (syncObject)
        {
            //read email
            emailTracker.add(email.identifier);
            //if all emails are read, clear the tracker
            //This will allow reading of all the emails 
            //once again.
            if(emailTracker.size()==noOfEmails)
            {
                emailTracker.clear();
            }

        }

答案 3 :(得分:1)

执行器可能不是这里的最佳解决方案,但为了简单起见,我将使用Gray代码的变体。为了连续扫描您的10个邮箱,您可以执行以下操作,但您必须添加一些代码来处理干净终止:

// Create one semaphore per mailbox
Semaphore semaphores[] = new Semaphore[10]
for (int s = 0; s < semaphores.length; s ++) {
    semaphores[s] = new Semaphore(1);
}

 // create a thread pool with 5 workers
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// submit all 10 user email accounts to the pool to be processed in turn
for (int i = 0; i < 5; i ++) {
    threadPool.submit(userEmailsToProcess, semaphores);
}
// once we have submitted all jobs to the thread pool, it should be shutdown
threadPool.shutdown();
...

// here's our runnable class which does the actual work
public class EmailProcessor implements Runnable {
    private UserEmail userEmailToProcess[];
    private Semaphore semaphores[];
    public MyJobProcessor(UserEmail userEmailToProcess[], Semaphore semaphores[]) {
        this.userEmailsToProcess = userEmailToProcess;
        this.semaphores = semaphores;
    }
    public void run() {
        while (true) {   // you could use a semaphore here to test program termination instead
            for (int s = 0; s < semaphores.size; s ++) {
                if (semaphores[s].tryAcquire()) {
                    UserEmail email = userEmailToProcess[s];
                    // read the user email
                    …
                    semaphores[s].release();
                }
            }
        }
    }
}

这是一个快速而肮脏的解决方案,不是100%公平,但它适用于任意数量的线程和邮箱。在您的特殊情况下,有10个电子邮件和5个工作人员,您可以让每个线程连续扫描邮箱的子集,即,thread1检查mailbox1然后mailbox2,线程2检查mailbox3然后mailbox4,...这样你可以没有信号量,因为那里没有争论

答案 4 :(得分:0)

Sanju我相信你想以下面的循环方式执行它们:

线程1:1,6等等 线程2:2,7 线程3:3,8 线程4:4,9 线程5:5,10

首先,如果你想按照这样的顺序执行它们,那么这不是什么线程。第二,我不认为线程池是可能的。如果您仍然需要它们,那么这里是我以循环方式执行线程的解决方案,您可以根据需要进行更改: 公共类EmailRoundRobin {         public Object [] locks;

    private static class EmailProcessor implements Runnable {
        private final Object currentLock;
        private final Object nextLock;
        private UserEmail userEmail;
        public EmailProcessor (UserEmail userEmail,, Object currentLock, Object nextLock) {
           this.userEmail = userEmail;
       this.currentLock = currentLock;
           this.nextLock = nextLock;
        }
        @Override
        public void run() {
           try {
                work = //reading email
                while ( work != null) {
                    try {
                        currentLock.wait();
                        // Do your work here.
                    }
                    catch(InterruptedException e) {}
                    synchronized(nextLock) {
                        nextLock.notify();
                    }
                }//while ends
            } catch (IOException e) {
                e.printStackTrace();
        }
        synchronized(nextLock) {
            nextLock.notify(); /// Ensures all threads exit at the end
        }
    }

public EmailRoundRobin(int numberOfAccountsToRead) {
    locks = new Object[numberOfAccountsToRead];

    //Initialize lock instances in array.
    for(i = 0; i < numberOfAccountsToRead; ++i) locks[i] = new Object();
    //Create threads
    int j;
    for(j=0; j<(numberOfAccountsToRead-1); j++ ){
        Thread linePrinterThread = new Thread(new EmailProcessor(emailInfo + "Temp" + j,locks[j],locks[j+1]));
        linePrinterThread.start();
    }
    Thread lastLinePrinterThread = new Thread(new EmailProcessor(emailInfo + "Temp" + j,locks[numberOfFilesToRead-1],locks[0]));
    lastLinePrinterThread.start();
}

public void startProcessing() {
    synchronized (locks[0]) {
        locks[0].notify();
    }
}

public static void main(String[] args) {
    EmailRoundRobin emailRoundRobin = new EmailRoundRobin(4);
    emailRoundRobin.startPrinting();
}

}

我从[这个问题](Running threads in round robin fashion in java)的回答中得到了这个结论!有类似的要求。在该答案中也提到了使用Phaser的选项。

答案 5 :(得分:-2)

我自己得到了一个答案,非常简单,我使用的是ExecutorService,它只由它管理。