This chapter relies on Marble Use. Make sure you have a good understanding of Marble before reading this chapter
Overview of Thread Pools
Since Marble is a framework project, user access to Marble does not care about the implementation mechanism of Marble.Therefore, Marble's consumption of resources should be controlled when doing related processing, and applications accessed cannot be made unavailable (e.g., resource exhaustion) because of Marble.
In addition, each time Marble-Agent receives an RPC schedule, a new thread is opened for JOB execution without blocking, and threads are frequently used, so Marble's resource use receipt must be made using the same thread pool.
Thread pool Java is well encapsulated and can be overwritten in most scenarios, as enumerated below:
1. newCachedThreadPool creates a cacheable thread pool that allows for flexible recycling of idle threads if the length of the thread pool exceeds processing requirements, or new threads if none is available;
2. newFixedThreadPool creates a fixed-length thread pool that controls the maximum number of concurrent threads, and the threads that exceed it wait in the queue;
3. newScheduledThreadPool creates a fixed-length thread pool that supports both periodic and periodic task execution;
4. newSingleThreadExecutor creates a single-threaded thread pool that only uses a single worker thread to perform tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority);
Thread pool new thread process (network theft):
Marble Thread Pool
Thread pool definition
Since Marble thread pools have a big role in controlling resource usage and capping Marble resource usage, Java itself provides a thread pool with a maximum number of threads, but blocking queues is unbounded and not suitable for resource-limited use.Therefore, Marble customizes the java thread pool.
Use bounded blocking queue
executor = new ThreadPoolExecutor( tpConfig.getMaxSize(), tpConfig.getCoreSize(), 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(tpConfig.getBlockQueueSize()), tpConfig.getRejectPolicy() );
Thread pool self-configuration support
To facilitate user self-configuration of thread pools, Marble provides configuration files to support user-defined thread pool configuration by creating a file marble-config.properties in the project root directory.Parameter assignments are made in the file as follows:
#Maximum Threads in Thread Pool tpool_max_size=5 #Number of thread pool core threads tpool_core_size=5 #Thread pool blocked bounded queue length tpool_bq_size=3 #Processing policy after the thread pool is full.1-AbortPolicy (throwing RejectedExecutionException); 2-CallerRunsPolicy; 3-DiscardOldestPolicy 4-DiscardPolicy (throwing no exception) tpool_reject_policy=1
Marble will first look for this configuration file in the root directory, and no default configuration will be found.
tpool_max_size=20
tpool_core_size=20
tpool_bq_size=5
tpool_reject_policy=1
Marble's configuration resolution classes are as follows:
/** * Marble Configuration Resolution * * @author <a href="dongjianxing@aliyun.com">jeff</a> * @version 2017/3/31 20:15 */ public class MarbleConfigParser { private static ClogWrapper logger = ClogWrapperFactory.getClogWrapper(MarbleConfigParser.class); private static final String CONFIG = "marble-config.properties"; private static Properties prop = new Properties(); //Default Configuration private static final int TPOOL_MAX_SIZE = 20;//Maximum Threads in Thread Pool private static final int TPOOL_CORE_SIZE = 20;//Number of thread pool core threads private static final int TPOOL_BQ_SIZE = 5;//Thread pool blocking queue size private static final int TPOOL_REJECT_POLICY = 1;//Processing policy for thread pool full. 1-AbortPolicy (throwing RejectedExecutionException exception); 2-CallerRunsPolicy; 3-DiscardOldestPolicy 4-DiscardPolicy private MarbleConfigParser() { try { InputStream stream = PropertyUtils.class.getClassLoader().getResourceAsStream(CONFIG); if (stream == null) { logger.MARK("PARSE_CONFIG").warn("no marbleConfig.properties.xml is exist in the root directory of classpath, so default the config will be used."); return; } prop.load(stream); } catch (Exception e) { logger.MARK("PARSE_CONFIG").error("parse the marbleConfig.properties.xml in the root directory exception, detail: {}", Throwables.getStackTraceAsString(e)); } } //Resolve thread pool configuration ThreadPoolConfig parseTPConfig() { ThreadPoolConfig tpConfig = null; try { Integer tpms = getInteger(prop, "tpool_max_size"); Integer tpcs = getInteger(prop, "tpool_core_size"); Integer tpqs = getInteger(prop, "tpool_bq_size"); Integer tprp = getInteger(prop, "tpool_reject_policy"); //Amendment Parameters tpcs = (tpcs == null || tpcs < 0 || tpcs > 500) ? TPOOL_CORE_SIZE : tpcs; tpms = (tpms == null || tpms < tpqs) ? tpcs : tpms; tpqs = (tpqs == null || tpqs < 0 || tpqs > 100) ? TPOOL_BQ_SIZE : tpqs; tprp = (tprp == null || tprp > 4) ? 1 : tprp; RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); switch (tprp) { case 1: handler = new ThreadPoolExecutor.AbortPolicy(); break; case 2: handler = new ThreadPoolExecutor.CallerRunsPolicy(); break; case 3: handler = new ThreadPoolExecutor.DiscardOldestPolicy(); break; case 4: handler = new ThreadPoolExecutor.DiscardPolicy(); break; } tpConfig = new ThreadPoolConfig(tpms,tpcs,tpqs,handler); } catch (Exception e) { logger.MARK("PARSE_CONFIG").error("parse the thread-pool config from marbleConfig.properties.xml exception, detail: {}", Throwables.getStackTraceAsString(e)); } if (tpConfig == null) { tpConfig = new ThreadPoolConfig(TPOOL_MAX_SIZE,TPOOL_CORE_SIZE, TPOOL_BQ_SIZE, new ThreadPoolExecutor.DiscardPolicy()); } return tpConfig; } private Integer getInteger(Properties prop, String key) { Integer result = null; try { String value = prop.getProperty(key); if (value != null && value.trim().length() > 0) { result = Integer.parseInt(value); } } catch (Exception e) { } return result; } //Singleton private static class SingletonHolder { private static final MarbleConfigParser CONFIG_HELPER = new MarbleConfigParser(); } public static MarbleConfigParser getInstance() { return MarbleConfigParser.SingletonHolder.CONFIG_HELPER; } //Thread pool configuration class ThreadPoolConfig { private int maxSize;//Maximum Threads in Thread Pool private int coreSize;//Number of thread pool core threads private int blockQueueSize;//Thread pool blocking queue size private RejectedExecutionHandler rejectPolicy;//Thread pool rejection policy ThreadPoolConfig(int maxSize, int coreSize, int blockQueueSize, RejectedExecutionHandler rejectPolicy) { this.maxSize = maxSize; this.coreSize = coreSize; this.blockQueueSize = blockQueueSize; this.rejectPolicy = rejectPolicy; } int getCoreSize() { return coreSize; } int getBlockQueueSize() { return blockQueueSize; } public int getMaxSize() { return maxSize; } RejectedExecutionHandler getRejectPolicy() { return rejectPolicy; } @Override public String toString() { return "ThreadPoolConfig{" + "maxSize=" + StringUtils.safeString(maxSize) + ", coreSize=" + StringUtils.safeString(coreSize) + ", blockQueueSize=" + StringUtils.safeString(blockQueueSize) + ", rejectPolicy=" + StringUtils.safeString(rejectPolicy.getClass().getSimpleName()) + '}'; } } }
Thread pool usage example
Take the following thread pool configuration as an example:
tpool_max_size=5
tpool_core_size=5
tpool_bq_size=3
tpool_reject_policy=1
In the figure below, the same machine (10.2.37.137) received 11 consecutive Marble dispatches>
- Marble-Agent successfully started five threads from the thread pool for execution on the first to fifth times.
- 6-8 calls, the number of core threads is full, bounded blocking queue begins to fill;
- The 9-10 call bounded blocking queues have been filled and the maximum number of threads has been filled. Due to the rejection policy Abort, 10-11 dispatch requests have been rejected directly.
- A Thread Interrupt call was made manually;
- Successfully executed for the 11th time;