ThreadPoolExecutor与ArrayBlockingQueue

时间:2012-05-26 22:47:59

标签: java multithreading threadpool executorservice

我开始从Java Doc中读取更多关于ThreadPoolExecutor的内容,因为我正在我的一个项目中使用它。那么有谁可以解释一下这条线实际上意味着什么? - 我知道每个参数代表什么,但我想从一些专家那里以更一般/非人的方式理解它。

ExecutorService service = new ThreadPoolExecutor(10, 10, 1000L,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10, true), new 
ThreadPoolExecutor.CallerRunsPolicy());

更新: - 问题陈述是: -

每个线程使用介于1和1000之间的唯一ID,程序必须运行60分钟或更长时间,因此在60分钟内,所有ID都可能完成,因此我需要再次重用这些ID。所以这是我使用上述执行程序编写的以下程序。

class IdPool {
    private final LinkedList<Integer> availableExistingIds = new LinkedList<Integer>();

    public IdPool() {
        for (int i = 1; i <= 1000; i++) {
            availableExistingIds.add(i);
        }
    }

    public synchronized Integer getExistingId() {
        return availableExistingIds.removeFirst();
    }

    public synchronized void releaseExistingId(Integer id) {
        availableExistingIds.add(id);
    }
}


class ThreadNewTask implements Runnable {
    private IdPool idPool;

    public ThreadNewTask(IdPool idPool) {
        this.idPool = idPool;
    }

    public void run() {
        Integer id = idPool.getExistingId();
        someMethod(id);
        idPool.releaseExistingId(id);
    }

// This method needs to be synchronized or not?
    private synchronized void someMethod(Integer id) {
        System.out.println("Task: " +id);
// and do other calcuations whatever you need to do in your program
    }
}

public class TestingPool {
    public static void main(String[] args) throws InterruptedException {
        int size = 10;
        int durationOfRun = 60;
        IdPool idPool = new IdPool();   
        // create thread pool with given size
        ExecutorService service = new ThreadPoolExecutor(size, size, 500L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(size), new ThreadPoolExecutor.CallerRunsPolicy()); 

        // queue some tasks
        long startTime = System.currentTimeMillis();
        long endTime = startTime + (durationOfRun * 60 * 1000L);

        // Running it for 60 minutes
        while(System.currentTimeMillis() <= endTime) {
            service.submit(new ThreadNewTask(idPool));
        }

        // wait for termination        
        service.shutdown();
        service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); 
    }
}

我的问题是: - 就性能是否考虑而言,此代码是正确的?还有什么我可以在这里使它更准确?任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:9)

[首先,我道歉,这是对先前答案的回应,但我想要格式化。)

除了实际情况之外,当项目提交给具有完整队列的ThreadPoolExecutor时,您不会阻止。原因是ThreadPoolExecutor调用BlockingQueue.offer(T item)方法,根据定义,该方法是一种非阻塞方法。它要么添加项目并返回true,要么不添加(填满时)并返回false。然后,ThreadPoolExecutor调用已注册的RejectedExecutionHandler来处理这种情况。

来自javadoc:

  

将来某个时间执行给定的任务。任务可以执行   在新线程或现有池线程中。如果任务不能   提交执行,要么是因为执行者已经执行了   关闭或因为已达到其容量,将处理该任务   由当前的RejectedExecutionHandler。

默认情况下,使用ThreadPoolExecutor.AbortPolicy()从ThreadPoolExecutor的“submit”或“execute”方法抛出RejectedExecutionException。

try {
   executorService.execute(new Runnable() { ... });
}
catch (RejectedExecutionException e) {
   // the queue is full, and you're using the AbortPolicy as the 
   // RejectedExecutionHandler
}

但是,您可以使用其他处理程序执行不同的操作,例如忽略错误(DiscardPolicy),或在调用“execute”或“submit”方法(CallerRunsPolicy)的线程中运行它。此示例允许在队列已满时调用“submit”或“execute”方法运行所请求任务的任何线程。 (这意味着在任何给定的时间,你可以在游泳池本身的顶部运行另外一件事):

ExecutorService service = new ThreadPoolExecutor(..., new ThreadPoolExecutor.CallerRunsPolicy());

如果你想阻塞和等待,你可以实现你自己的RejectedExecutionHandler,它会阻塞,直到队列中有一个可用的插槽(这是一个粗略的估计,我没有编译或运行它,但你应该得到这个想法) :

public class BlockUntilAvailableSlot implements RejectedExecutionHandler {
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     if (e.isTerminated() || e.isShutdown()) {
        return;
     }

     boolean submitted = false;
     while (! submitted) {
       if (Thread.currentThread().isInterrupted()) {
            // be a good citizen and do something nice if we were interrupted
            // anywhere other than during the sleep method.
       }

       try {
          e.execute(r);
          submitted = true;
       }
       catch (RejectedExceptionException e) {
         try {
           // Sleep for a little bit, and try again.
           Thread.sleep(100L);
         }
         catch (InterruptedException e) {
           ; // do you care if someone called Thread.interrupt?
           // if so, do something nice here, and maybe just silently return.
         }
       }
     }
  }
}

答案 1 :(得分:2)

它正在创建一个ExecutorService来处理线程池的执行。在这种情况下,池中的初始和最大线程数均为10。当池中的线程空闲1秒钟(1000ms)时它将终止它(空闲计时器),但是因为线程的最大和核心数量是相同的,所以这永远不会发生(它总是保持10个线程并且将会从不运行超过10个线程)。

它使用ArrayBlockingQueue来管理10个插槽的执行请求,因此当队列已满(10个线程入队后)时,它将阻止调用者。

如果线程被拒绝(在这种情况下,由于服务关闭,由于线程将排队,或者如果队列已满,您将在排队线程时被阻止),那么提供的Runnable将在调用者的主题上执行。

答案 2 :(得分:0)

考虑信号量。这些是出于同样的目的。请在下面查看使用信号量的代码。不确定这是不是你想要的。但如果没有更多的许可证,这将阻止。对你来说ID也很重要吗?

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class ThreadNewTask implements Runnable {
    private Semaphore idPool;

    public ThreadNewTask(Semaphore idPool) {
        this.idPool = idPool;
    }

    public void run() {
//      Integer id = idPool.getExistingId();
        try {
            idPool.acquire();
            someMethod(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            idPool.release();
        }
//      idPool.releaseExistingId(id);
    }

    // This method needs to be synchronized or not?
    private void someMethod(Integer id) {
        System.out.println("Task: " + id);
        // and do other calcuations whatever you need to do in your program
    }
}

public class TestingPool {
    public static void main(String[] args) throws InterruptedException {
        int size = 10;
        int durationOfRun = 60;
        Semaphore idPool = new Semaphore(100); 
//      IdPool idPool = new IdPool();
        // create thread pool with given size
        ExecutorService service = new ThreadPoolExecutor(size, size, 500L,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(size),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // queue some tasks
        long startTime = System.currentTimeMillis();
        long endTime = startTime + (durationOfRun * 60 * 1000L);

        // Running it for 60 minutes
        while (System.currentTimeMillis() <= endTime) {
            service.submit(new ThreadNewTask(idPool));
        }

        // wait for termination
        service.shutdown();
        service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    }
}

答案 3 :(得分:0)

另一种解决方案是破解底层队列,用 offer 替换 offer,超时时间较长(最多 292 年,可以认为是无限的)。


// helper method
private static boolean interruptibleInfiniteOffer(BlockingQueue<Runnable> q, Runnable r) {
    try {
        return q.offer(r, Long.MAX_VALUE, TimeUnit.NANOSECONDS); // infinite == ~292 years
    } catch (InterruptedException e) {
        return false;
    }
}

// fixed size pool with blocking (instead of rejecting) if bounded queue is full
public static ThreadPoolExecutor getFixedSizePoolWithLimitedWaitingQueue(int nThreads, int maxItemsInTheQueue) {
    BlockingQueue<Runnable> queue = maxItemsInTheQueue == 0
            ? new SynchronousQueue<>() { public boolean offer(Runnable r) { return interruptibleInfiniteOffer(this, r);} }
            : new ArrayBlockingQueue<>(maxItemsInTheQueue) { public boolean offer(Runnable r) { return interruptibleInfiniteOffer(this, r);} };
    return new ThreadPoolExecutor(nThreads, nThreads, 0, TimeUnit.MILLISECONDS, queue);
}