Created
December 13, 2016 21:04
-
-
Save Hervian/2987085c6ea5b5227ba7f5b506663432 to your computer and use it in GitHub Desktop.
A Java service class for creating and managing background threads as WeakReferences
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.github.hervian.util; | |
| import java.lang.ref.WeakReference; | |
| import java.util.Hashtable; | |
| import java.util.Map; | |
| import java.util.concurrent.Executors; | |
| import java.util.concurrent.ScheduledFuture; | |
| import java.util.concurrent.ScheduledThreadPoolExecutor; | |
| import java.util.concurrent.ThreadFactory; | |
| import java.util.concurrent.TimeUnit; | |
| public enum PeriodicEventHelper { | |
| INSTANCE; | |
| private static final int CORE_THREAD_POOL_SIZE = 1; | |
| private ScheduledThreadPoolExecutor daemonThreadBasedScheduler; | |
| private final Map<WeakReference<PeriodicEventDefiner>, ScheduledFuture<?>> mapFromPeriodicEventsToScheduledFutures = new Hashtable<>(); | |
| /** | |
| * This service wraps the argument provided {@link PeriodicEventDefiner} in a | |
| * WeakReference, and starts a background job, as specified by the definer. | |
| * The job will be terminated/removed once the PeriodicDefiner has been | |
| * garbage collected. A typical job is one that refreshes a cache every X | |
| * minutes. <br> | |
| * Using the {@link PeriodicEventHelper} offers the following advantage over | |
| * manually creating a new Thread for each job: | |
| * <ul> | |
| * <li>In essence, we only have one Thread which handles our background jobs. | |
| * If need be (if the jobs are scheduled to run at the same time) new Threads | |
| * are created on the fly, and destroyed afterwards. | |
| * </ul> | |
| * | |
| * @param periodicEventDefiner | |
| * A bean that defines a task, and an interval. The task is used in a | |
| * recurring job and the time between each execution is defined by | |
| * the interval. The interval is described as a unit type and a | |
| * number of units. | |
| */ | |
| public void register(PeriodicEventDefiner periodicEventDefiner) { | |
| if (periodicEventDefiner != null && periodicEventDefiner.getPeriodEventInterval() != null) { | |
| WeakReference<PeriodicEventDefiner> weakReferenceToPeriodicEvent = new WeakReference<PeriodicEventDefiner>( | |
| periodicEventDefiner); | |
| ScheduledFuture<?> scheduledFuture = createAndScheduleRunnable(periodicEventDefiner, | |
| weakReferenceToPeriodicEvent); | |
| mapFromPeriodicEventsToScheduledFutures.put(weakReferenceToPeriodicEvent, scheduledFuture); | |
| } | |
| } | |
| private ScheduledFuture<?> createAndScheduleRunnable(PeriodicEventDefiner periodicEventDefiner, | |
| WeakReference<PeriodicEventDefiner> weakReferenceToPeriodicEvent) { | |
| Runnable command = wrapEventInRunnable(weakReferenceToPeriodicEvent); | |
| PeriodEventInterval interval = periodicEventDefiner.getPeriodEventInterval(); | |
| return scheduleRunnable(command, interval); | |
| } | |
| private ScheduledFuture<?> scheduleRunnable(Runnable command, PeriodEventInterval interval) { | |
| ScheduledThreadPoolExecutor scheduler = getScheduler(); | |
| return scheduler.scheduleWithFixedDelay(command, interval.getInitialDelay(), interval.getDelay(), | |
| interval.getTimeUnit()); | |
| } | |
| private Runnable wrapEventInRunnable(final WeakReference<PeriodicEventDefiner> weakReferenceToPeriodicEvent) { | |
| return new Runnable() { | |
| @Override | |
| public void run() { | |
| PeriodicEventDefiner periodicEventDefiner = weakReferenceToPeriodicEvent.get(); | |
| if (periodicEventDefiner != null) { | |
| periodicEventDefiner.executePeriodicEvent(); | |
| } else { | |
| cancelAndRemovePeriodicEvent(weakReferenceToPeriodicEvent); //PeriodicEventDefiner has been garbage collected. Cancel thread and remove map entry. | |
| } | |
| } | |
| }; | |
| } | |
| private void cancelAndRemovePeriodicEvent(final WeakReference<PeriodicEventDefiner> weakReferenceToPeriodicEvent) { | |
| ScheduledFuture<?> scheduledFuture = mapFromPeriodicEventsToScheduledFutures.get(weakReferenceToPeriodicEvent); | |
| if (scheduledFuture != null) { | |
| scheduledFuture.cancel(true); | |
| mapFromPeriodicEventsToScheduledFutures.remove(weakReferenceToPeriodicEvent); | |
| } | |
| } | |
| private ScheduledThreadPoolExecutor getScheduler() { | |
| if (daemonThreadBasedScheduler == null) { | |
| daemonThreadBasedScheduler = new ScheduledThreadPoolExecutor(CORE_THREAD_POOL_SIZE, new ThreadFactory() { | |
| @Override | |
| public Thread newThread(Runnable runnable) { | |
| Thread daemonThread = Executors.defaultThreadFactory().newThread(runnable); | |
| daemonThread.setDaemon(true); | |
| return daemonThread; | |
| } | |
| }); | |
| daemonThreadBasedScheduler.setRemoveOnCancelPolicy(true); | |
| } | |
| return daemonThreadBasedScheduler; | |
| } | |
| public static interface PeriodicEventDefiner { | |
| void executePeriodicEvent(); | |
| PeriodEventInterval getPeriodEventInterval(); | |
| } | |
| public static class PeriodEventInterval { | |
| private long initialDelay; | |
| private TimeUnit timeUnit; | |
| private long delay; | |
| public PeriodEventInterval(long initialDelay, TimeUnit timeUnit, long delay) { | |
| this.initialDelay = initialDelay; | |
| this.timeUnit = timeUnit; | |
| this.delay = delay; | |
| } | |
| public long getInitialDelay() { | |
| return initialDelay; | |
| } | |
| public TimeUnit getTimeUnit() { | |
| return timeUnit; | |
| } | |
| public long getDelay() { | |
| return delay; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment