001package org.cache2k.impl;
002
003/*
004 * #%L
005 * cache2k core package
006 * %%
007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the 
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public 
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.util.concurrent.RejectedExecutionException;
026import java.util.concurrent.SynchronousQueue;
027import java.util.concurrent.ThreadFactory;
028import java.util.concurrent.ThreadPoolExecutor;
029import java.util.concurrent.TimeUnit;
030import java.util.concurrent.atomic.AtomicInteger;
031
032/**
033 * Provides a shared thread pool used by all caches for background refreshes of expired
034 * entries. The maximum thread size is the processor count times two.
035 *
036 * @author Jens Wilke; created: 2013-06-13
037 */
038public class CacheRefreshThreadPool {
039
040  public static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors() * 2;
041  private static ThreadPoolExecutor executorForAll;
042  private static int leasedPoolInstances = 0;
043  private static MyStatus status;
044
045  /**
046   * Get an instance of the pool. When the consumer is destroyed it must
047   * call {@link #destroy()} in turn to free resources.
048   */
049  public synchronized static CacheRefreshThreadPool getInstance() {
050    if (executorForAll == null) {
051      executorForAll =
052        new ThreadPoolExecutor(0, THREAD_COUNT,
053          21, TimeUnit.SECONDS,
054          new SynchronousQueue<Runnable>(),
055          new MyThreadFactory(),
056          new ThreadPoolExecutor.AbortPolicy());
057    }
058    leasedPoolInstances++;
059    CacheRefreshThreadPool p = new CacheRefreshThreadPool();
060    p.executor = executorForAll;
061    return p;
062  }
063
064  synchronized static void disposeOne() {
065    leasedPoolInstances--;
066    if (leasedPoolInstances == 0) {
067      executorForAll.shutdown();
068      executorForAll = null;
069    }
070  }
071
072  private ThreadPoolExecutor executor;
073
074  private CacheRefreshThreadPool() {
075  }
076
077  public boolean submit(Runnable r) {
078    try {
079      executor.execute(r);
080    } catch (RejectedExecutionException e) {
081      return false;
082    }
083    return true;
084  }
085
086  public void destroy() {
087    disposeOne();
088    executor = null;
089  }
090
091  static class MyThreadFactory implements ThreadFactory {
092
093    AtomicInteger count = new AtomicInteger();
094
095    @Override
096    public synchronized Thread newThread(Runnable r) {
097      Thread t = new Thread(r, "cache-refresh-" + count.incrementAndGet());
098      t.setDaemon(true);
099      return t;
100    }
101
102  }
103
104  static class MyStatus {
105    public String toString() {
106      return "CacheRefreshThreadPool(" +
107              "size=" + executorForAll.getPoolSize() + ", " +
108              "sizeLargest=" + executorForAll.getLargestPoolSize() + ", " +
109              "sizeMax=" + executorForAll.getMaximumPoolSize() + ", " +
110              "taskCount=" + executorForAll.getTaskCount() + ")";
111    }
112  }
113
114}