001package com.pi4j.concurrent; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : DefaultExecutorServiceFactory.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: http://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2015 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032 033import java.util.ArrayList; 034import java.util.List; 035import java.util.concurrent.Executor; 036import java.util.concurrent.ExecutorService; 037import java.util.concurrent.Executors; 038import java.util.concurrent.ScheduledExecutorService; 039import java.util.concurrent.ThreadFactory; 040import java.util.concurrent.atomic.AtomicLong; 041 042public class DefaultExecutorServiceFactory implements ExecutorServiceFactory { 043 044 public static int MAX_THREADS_IN_POOL = 25; 045 private static List<ExecutorService> singleThreadExecutorServices = new ArrayList<>(); 046 047 // this seemingly odd pattern is the recommended way to lazy-initialize static fields in effective java. 048 // The static "holder" class doesn't have it's static initializer called until it is accessed - and it's not accessed until the 049 // getInternalScheduledExecutor() method is called. 050 // 051 // (see effective java item 71:Use lazy initialization judiciously) 052 private static class ScheduledExecutorServiceHolder { 053 static final ScheduledExecutorService heldExecutor = Executors.newScheduledThreadPool(MAX_THREADS_IN_POOL, getThreadFactory("pi4j-scheduled-executor-%d")); 054 } 055 private static ScheduledExecutorService getInternalScheduledExecutorService() { 056 return ScheduledExecutorServiceHolder.heldExecutor; 057 } 058 private static class ScheduledExecutorServiceWrapperHolder { 059 static final ScheduledExecutorServiceWrapper heldWrapper = new ScheduledExecutorServiceWrapper(getInternalScheduledExecutorService()); 060 } 061 private static ScheduledExecutorServiceWrapper getScheduledExecutorServiceWrapper() { 062 return ScheduledExecutorServiceWrapperHolder.heldWrapper; 063 } 064 065 // follow a similar lazy initialization pattern for the gpio events 066 private static class GpioEventExecutorServiceHolder { 067 static final ExecutorService heldExecutor = Executors.newCachedThreadPool(getThreadFactory("pi4j-gpio-event-executor-%d")); 068 } 069 private static ExecutorService getInternalGpioExecutorService() { 070 return GpioEventExecutorServiceHolder.heldExecutor; 071 } 072 private static class GpioEventExecutorServiceWrapperHolder { 073 static final ExecutorService heldWrapper = new ShutdownDisabledExecutorWrapper(getInternalGpioExecutorService()); 074 } 075 private static ExecutorService getGpioEventExecutorServiceWrapper() { 076 return GpioEventExecutorServiceWrapperHolder.heldWrapper; 077 } 078 079 /** 080 * return an instance to the thread factory used to create new executor services 081 */ 082 private static ThreadFactory getThreadFactory(final String nameFormat) { 083 final ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory(); 084 return new ThreadFactory() { 085 final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; 086 087 @Override 088 public Thread newThread(Runnable runnable) { 089 Thread thread = defaultThreadFactory.newThread(runnable); 090 if (nameFormat != null) { 091 thread.setName(String.format(nameFormat, count.getAndIncrement())); 092 } 093 return thread; 094 } 095 }; 096 } 097 098 /** 099 * return an instance to the scheduled executor service (wrapper) 100 */ 101 public ScheduledExecutorService getScheduledExecutorService() { 102 // we return the protected wrapper to prevent any consumers from 103 // being able to shutdown the scheduled executor service 104 return getScheduledExecutorServiceWrapper(); 105 } 106 107 @Override 108 public ExecutorService getGpioEventExecutorService() { 109 // we return the protected wrapper to prevent any consumers from 110 // being able to shutdown the scheduled executor service 111 return getGpioEventExecutorServiceWrapper(); 112 } 113 114 /** 115 * return a new instance of a single thread executor service 116 * 117 * This method is deprecated in favor of the getGpioEventExecutorService - which provides better guarantees around resource 118 * management 119 */ 120 @Override 121 public ExecutorService newSingleThreadExecutorService() { 122 123 // create new single thread executor service 124 ExecutorService singleThreadExecutorService = Executors.newSingleThreadExecutor(getThreadFactory("pi4j-single-executor-%d")); 125 126 // add new instance to managed collection 127 singleThreadExecutorServices.add(singleThreadExecutorService); 128 129 // return new executor service 130 return singleThreadExecutorService; 131 } 132 133 /** 134 * shutdown executor threads 135 */ 136 public void shutdown() { 137 // shutdown each single thread executor in the managed collection 138 for (ExecutorService singleThreadExecutorService : singleThreadExecutorServices) { 139 shutdownExecutor(singleThreadExecutorService); 140 } 141 142 // shutdown scheduled executor instance 143 shutdownExecutor(getInternalScheduledExecutorService()); 144 shutdownExecutor(getInternalGpioExecutorService()); 145 146 } 147 148 private void shutdownExecutor(ExecutorService executor) { 149 if (executor != null) { 150 if (!executor.isShutdown()) { 151 // this is a forceful shutdown; 152 // don't wait for the scheduled tasks to complete 153 executor.shutdownNow(); 154 } 155 } 156 } 157}