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: https://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2021 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.ExecutorService; 036import java.util.concurrent.Executors; 037import java.util.concurrent.ScheduledExecutorService; 038import java.util.concurrent.ThreadFactory; 039import java.util.concurrent.atomic.AtomicLong; 040 041public class DefaultExecutorServiceFactory implements ExecutorServiceFactory { 042 043 private static int MAX_THREADS_IN_POOL = 25; 044 private static List<ExecutorService> singleThreadExecutorServices = new ArrayList<>(); 045 046 // this seemingly odd pattern is the recommended way to lazy-initialize static fields in effective java. 047 // The static "holder" class doesn't have it's static initializer called until it is accessed - and it's not accessed until the 048 // getInternalScheduledExecutor() method is called. 049 // 050 // (see effective java item 71:Use lazy initialization judiciously) 051 private static class ScheduledExecutorServiceHolder { 052 static final ScheduledExecutorService heldExecutor = Executors.newScheduledThreadPool(MAX_THREADS_IN_POOL, getThreadFactory("pi4j-scheduled-executor-%d")); 053 } 054 private static ScheduledExecutorService getInternalScheduledExecutorService() { 055 return ScheduledExecutorServiceHolder.heldExecutor; 056 } 057 private static class ScheduledExecutorServiceWrapperHolder { 058 static final ScheduledExecutorServiceWrapper heldWrapper = new ScheduledExecutorServiceWrapper(getInternalScheduledExecutorService()); 059 } 060 private static ScheduledExecutorServiceWrapper getScheduledExecutorServiceWrapper() { 061 return ScheduledExecutorServiceWrapperHolder.heldWrapper; 062 } 063 064 // follow a similar lazy initialization pattern for the gpio events 065 private static class GpioEventExecutorServiceHolder { 066 static final ExecutorService heldExecutor = Executors.newCachedThreadPool(getThreadFactory("pi4j-gpio-event-executor-%d")); 067 } 068 private static ExecutorService getInternalGpioExecutorService() { 069 return GpioEventExecutorServiceHolder.heldExecutor; 070 } 071 private static class GpioEventExecutorServiceWrapperHolder { 072 static final ExecutorService heldWrapper = new ShutdownDisabledExecutorWrapper(getInternalGpioExecutorService()); 073 } 074 private static ExecutorService getGpioEventExecutorServiceWrapper() { 075 return GpioEventExecutorServiceWrapperHolder.heldWrapper; 076 } 077 078 /** 079 * return an instance to the thread factory used to create new executor services 080 */ 081 private static ThreadFactory getThreadFactory(final String nameFormat) { 082 final ThreadFactory defaultThreadFactory = Executors.privilegedThreadFactory(); 083 return new ThreadFactory() { 084 final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; 085 086 @Override 087 public Thread newThread(Runnable runnable) { 088 Thread thread = defaultThreadFactory.newThread(runnable); 089 if (nameFormat != null) { 090 thread.setName(String.format(nameFormat, count.getAndIncrement())); 091 } 092 return thread; 093 } 094 }; 095 } 096 097 /** 098 * return an instance to the scheduled executor service (wrapper) 099 */ 100 public ScheduledExecutorService getScheduledExecutorService() { 101 // we return the protected wrapper to prevent any consumers from 102 // being able to shutdown the scheduled executor service 103 return getScheduledExecutorServiceWrapper(); 104 } 105 106 @Override 107 public ExecutorService getGpioEventExecutorService() { 108 // we return the protected wrapper to prevent any consumers from 109 // being able to shutdown the scheduled executor service 110 return getGpioEventExecutorServiceWrapper(); 111 } 112 113 /** 114 * return a new instance of a single thread executor service 115 * 116 * This method is deprecated in favor of the getGpioEventExecutorService - which provides better guarantees around resource 117 * management 118 */ 119 @Override 120 public ExecutorService newSingleThreadExecutorService() { 121 122 // create new single thread executor service 123 ExecutorService singleThreadExecutorService = Executors.newSingleThreadExecutor(getThreadFactory("pi4j-single-executor-%d")); 124 125 // add new instance to managed collection 126 singleThreadExecutorServices.add(singleThreadExecutorService); 127 128 // return new executor service 129 return singleThreadExecutorService; 130 } 131 132 /** 133 * shutdown executor threads 134 */ 135 public void shutdown() { 136 // shutdown each single thread executor in the managed collection 137 for (ExecutorService singleThreadExecutorService : singleThreadExecutorServices) { 138 shutdownExecutor(singleThreadExecutorService); 139 } 140 141 // shutdown scheduled executor instance 142 shutdownExecutor(getInternalScheduledExecutorService()); 143 shutdownExecutor(getInternalGpioExecutorService()); 144 145 } 146 147 private void shutdownExecutor(ExecutorService executor) { 148 if (executor != null) { 149 if (!executor.isShutdown()) { 150 // this is a forceful shutdown; 151 // don't wait for the scheduled tasks to complete 152 executor.shutdownNow(); 153 } 154 } 155 } 156}