001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.lang3.concurrent; 019 020import java.util.ArrayDeque; 021import java.util.Deque; 022import java.util.concurrent.ScheduledExecutorService; 023import java.util.concurrent.ScheduledFuture; 024import java.util.concurrent.ScheduledThreadPoolExecutor; 025import java.util.concurrent.TimeUnit; 026import java.util.function.Supplier; 027 028import org.apache.commons.lang3.Validate; 029 030/** 031 * A specialized <em>semaphore</em> implementation that provides a number of permits in a given time frame. 032 * 033 * <p> 034 * This class is similar to the {@link java.util.concurrent.Semaphore} class provided by the JDK in that it manages a configurable number of permits. Using the 035 * {@link #acquire()} method a permit can be requested by a thread. However, there is an additional timing dimension: there is no {@code 036 * release()} method for freeing a permit, but all permits are automatically released at the end of a configurable time frame. If a thread calls 037 * {@link #acquire()} and the available permits are already exhausted for this time frame, the thread is blocked. When the time frame ends all permits requested 038 * so far are restored, and blocking threads are waked up again, so that they can try to acquire a new permit. This basically means that in the specified time 039 * frame only the given number of operations is possible. 040 * </p> 041 * <p> 042 * A use case for this class is to artificially limit the load produced by a process. As an example consider an application that issues database queries on a 043 * production system in a background process to gather statistical information. This background processing should not produce so much database load that the 044 * functionality and the performance of the production system are impacted. Here a {@link TimedSemaphore} could be installed to guarantee that only a given 045 * number of database queries are issued per second. 046 * </p> 047 * <p> 048 * A thread class for performing database queries could look as follows: 049 * </p> 050 * 051 * <pre> 052 * public class StatisticsThread extends Thread { 053 * // The semaphore for limiting database load. 054 * private final TimedSemaphore semaphore; 055 * // Create an instance and set the semaphore 056 * public StatisticsThread(TimedSemaphore timedSemaphore) { 057 * semaphore = timedSemaphore; 058 * } 059 * // Gather statistics 060 * public void run() { 061 * try { 062 * while (true) { 063 * semaphore.acquire(); // limit database load 064 * performQuery(); // issue a query 065 * } 066 * } catch (InterruptedException) { 067 * // fall through 068 * } 069 * } 070 * ... 071 * } 072 * </pre> 073 * 074 * <p> 075 * The following code fragment shows how a {@link TimedSemaphore} is created that allows only 10 operations per second and passed to the statistics thread: 076 * </p> 077 * 078 * <pre> 079 * TimedSemaphore sem = new TimedSemaphore(1, TimeUnit.SECOND, 10); 080 * StatisticsThread thread = new StatisticsThread(sem); 081 * thread.start(); 082 * </pre> 083 * 084 * <p> 085 * When creating an instance the time period for the semaphore must be specified. {@link TimedSemaphore} uses an executor service with a corresponding period to 086 * monitor this interval. The {@code 087 * ScheduledExecutorService} to be used for this purpose can be provided at construction time. Alternatively the class creates an internal executor service. 088 * </p> 089 * <p> 090 * Client code that uses {@link TimedSemaphore} has to call the {@link #acquire()} method in each processing step. {@link TimedSemaphore} keeps track of the 091 * number of invocations of the {@link #acquire()} method and blocks the calling thread if the counter exceeds the limit specified. When the timer signals the 092 * end of the time period the counter is reset and all waiting threads are released. Then another cycle can start. 093 * </p> 094 * <p> 095 * An alternative to {@code acquire()} is the {@link #tryAcquire()} method. This method checks whether the semaphore is under the specified limit and increases 096 * the internal counter if this is the case. The return value is then <strong>true</strong>, and the calling thread can continue with its action. If the 097 * semaphore is already at its limit, {@code tryAcquire()} immediately returns <strong>false</strong> without blocking; the calling thread must then abort its 098 * action. This usage scenario prevents blocking of threads. 099 * </p> 100 * <p> 101 * It is possible to modify the limit at any time using the {@link #setLimit(int)} method. This is useful if the load produced by an operation has to be adapted 102 * dynamically. In the example scenario with the thread collecting statistics it may make sense to specify a low limit during day time while allowing a higher 103 * load in the night time. Reducing the limit takes effect immediately by blocking incoming callers. If the limit is increased, waiting threads are not released 104 * immediately, but wake up when the timer runs out. Then, in the next period more processing steps can be performed without blocking. By setting the limit to 0 105 * the semaphore can be switched off: in this mode the {@link #acquire()} method never blocks, but lets all callers pass directly. 106 * </p> 107 * <p> 108 * When the {@link TimedSemaphore} is no more needed its {@link #shutdown()} method should be called. This causes the periodic task that monitors the time 109 * interval to be canceled. If the {@link ScheduledExecutorService} has been created by the semaphore at construction time, it is also shut down. resources. 110 * After that {@link #acquire()} must not be called any more. 111 * </p> 112 * 113 * @since 3.0 114 */ 115public class TimedSemaphore { 116 117 /** 118 * Builds new {@link TimedSemaphore}. 119 * 120 * @since 3.20.0 121 */ 122 public static class Builder implements Supplier<TimedSemaphore> { 123 124 private ScheduledExecutorService service; 125 private long period; 126 private TimeUnit timeUnit; 127 private int limit; 128 private boolean fair; 129 130 /** 131 * Constructs a new Builder. 132 */ 133 public Builder() { 134 // empty 135 } 136 137 @Override 138 public TimedSemaphore get() { 139 return new TimedSemaphore(this); 140 } 141 142 /** 143 * Sets whether blocking acquisitions use FIFO ordering. 144 * 145 * <p> 146 * Fairness is disabled by default. When enabled, calls to {@link TimedSemaphore#acquire()} are ordered by entry into the internal waiting queue, 147 * not by thread start time or method return order. New calls made with the limit disabled and calls to {@link TimedSemaphore#tryAcquire()} may 148 * bypass waiting callers. 149 * </p> 150 * 151 * @param fair Whether to enable FIFO ordering. 152 * @return {@code this} instance. 153 * @since 3.22.0 154 */ 155 public Builder setFair(final boolean fair) { 156 this.fair = fair; 157 return this; 158 } 159 160 /** 161 * Sets the limit. 162 * 163 * @param limit The limit. 164 * @return {@code this} instance. 165 */ 166 public Builder setLimit(final int limit) { 167 this.limit = limit; 168 return this; 169 } 170 171 /** 172 * Sets the time period. 173 * 174 * @param period The time period. 175 * @return {@code this} instance. 176 */ 177 public Builder setPeriod(final long period) { 178 this.period = period; 179 return this; 180 } 181 182 /** 183 * Sets the executor service. 184 * 185 * @param service The executor service. 186 * @return {@code this} instance. 187 */ 188 public Builder setService(final ScheduledExecutorService service) { 189 this.service = service; 190 return this; 191 } 192 193 /** 194 * Sets the time unit for the period. 195 * 196 * @param timeUnit The time unit for the period. 197 * @return {@code this} instance. 198 */ 199 public Builder setTimeUnit(final TimeUnit timeUnit) { 200 this.timeUnit = timeUnit; 201 return this; 202 } 203 } 204 205 /** 206 * Constant for a value representing no limit. If the limit is set to a value less or equal this constant, the {@link TimedSemaphore} will be effectively 207 * switched off. 208 */ 209 public static final int NO_LIMIT = 0; 210 211 /** Constant for the thread pool size for the executor. */ 212 private static final int THREAD_POOL_SIZE = 1; 213 214 /** 215 * Constructs a new Builder. 216 * 217 * @return A new Builder. 218 * @since 3.20.0 219 */ 220 public static Builder builder() { 221 return new Builder(); 222 } 223 224 /** The executor service for managing the timer thread. */ 225 private final ScheduledExecutorService executorService; 226 227 /** The period for this timed semaphore. */ 228 private final long period; 229 230 /** The time unit for the period. */ 231 private final TimeUnit unit; 232 233 /** A flag whether the executor service was created by this object. */ 234 private final boolean ownExecutor; 235 236 /** A future object representing the timer task. */ 237 private ScheduledFuture<?> task; // @GuardedBy("this") 238 239 /** Stores the total number of invocations of the acquire() method. */ 240 private long totalAcquireCount; // @GuardedBy("this") 241 242 /** 243 * The counter for the periods. This counter is increased every time a period ends. 244 */ 245 private long periodCount; // @GuardedBy("this") 246 247 /** The limit. */ 248 private int limit; // @GuardedBy("this") 249 250 /** The current counter. */ 251 private int acquireCount; // @GuardedBy("this") 252 253 /** The number of invocations of acquire() in the last period. */ 254 private int lastCallsPerPeriod; // @GuardedBy("this") 255 256 /** A flag whether shutdown() was called. */ 257 private boolean shutdown; // @GuardedBy("this") 258 259 /** Whether blocking acquisitions use FIFO ordering. */ 260 private final boolean fair; 261 262 /** Tokens for fair acquisitions, guarded by this object's monitor. */ 263 private final Deque<Object> waiters = new ArrayDeque<>(); 264 265 private TimedSemaphore(final Builder builder) { 266 Validate.inclusiveBetween(1, Long.MAX_VALUE, builder.period, "Time period must be greater than 0."); 267 fair = builder.fair; 268 period = builder.period; 269 unit = builder.timeUnit; 270 if (builder.service != null) { 271 executorService = builder.service; 272 ownExecutor = false; 273 } else { 274 final ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(THREAD_POOL_SIZE); 275 stpe.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); 276 stpe.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); 277 executorService = stpe; 278 ownExecutor = true; 279 } 280 setLimit(builder.limit); 281 } 282 283 /** 284 * Constructs a new instance of {@link TimedSemaphore} and initializes it with the given time period and the limit. 285 * 286 * @param timePeriod The time period. 287 * @param timeUnit The unit for the period. 288 * @param limit The limit for the semaphore. 289 * @throws IllegalArgumentException Thrown if the period is less or equals 0. 290 * @deprecated Use {@link #builder()} and {@link Builder}. 291 */ 292 @Deprecated 293 public TimedSemaphore(final long timePeriod, final TimeUnit timeUnit, final int limit) { 294 this(null, timePeriod, timeUnit, limit); 295 } 296 297 /** 298 * Constructs a new instance of {@link TimedSemaphore} and initializes it with an executor service, the given time period, and the limit. The executor service 299 * will be used for creating a periodic task for monitoring the time period. It can be {@code null}, then a default service will be created. 300 * 301 * @param service The executor service. 302 * @param timePeriod The time period. 303 * @param timeUnit The unit for the period. 304 * @param limit The limit for the semaphore. 305 * @throws IllegalArgumentException Thrown if the period is less or equals 0. 306 * @deprecated Use {@link #builder()} and {@link Builder}. 307 */ 308 @Deprecated 309 public TimedSemaphore(final ScheduledExecutorService service, final long timePeriod, final TimeUnit timeUnit, final int limit) { 310 this(builder().setService(service).setPeriod(timePeriod).setTimeUnit(timeUnit).setLimit(limit)); 311 } 312 313 /** 314 * Acquires a permit from this semaphore. This method will block if the limit for the current period has already been reached. If {@link #shutdown()} has 315 * already been invoked, calling this method will cause an exception. The very first call of this method starts the timer task which monitors the time 316 * period set for this {@link TimedSemaphore}. From now on the semaphore is active. 317 * 318 * <p> 319 * When fairness is enabled, waiting callers acquire permits in the order they enter the internal queue. This does not guarantee thread start or 320 * method return order. New calls made with the limit disabled do not wait for queued callers. Calls to {@link #tryAcquire()} may also bypass 321 * queued callers. 322 * </p> 323 * 324 * @throws InterruptedException Thrown if the thread gets interrupted. 325 * @throws IllegalStateException Thrown if this semaphore is already shut down. 326 */ 327 public synchronized void acquire() throws InterruptedException { 328 prepareAcquire(); 329 if (fair && getLimit() > NO_LIMIT) { 330 // A private token avoids invoking an overridden Thread.equals() during cancellation. 331 final Object waiter = new Object(); 332 waiters.addLast(waiter); 333 try { 334 while (waiters.peekFirst() != waiter || !acquirePermit()) { 335 wait(); 336 if (shutdown) { 337 throw new IllegalStateException("TimedSemaphore is shut down."); 338 } 339 } 340 } finally { 341 waiters.removeFirstOccurrence(waiter); 342 // Let the next caller use any permits remaining in this period, including after interruption. 343 notifyAll(); 344 } 345 } else { 346 boolean canPass; 347 do { 348 canPass = acquirePermit(); 349 if (!canPass) { 350 wait(); 351 if (shutdown) { 352 throw new IllegalStateException("TimedSemaphore is shut down."); 353 } 354 } 355 } while (!canPass); 356 } 357 } 358 359 /** 360 * Internal helper method for acquiring a permit. This method checks whether currently a permit can be acquired and - if so - increases the internal 361 * counter. The return value indicates whether a permit could be acquired. This method must be called with the lock of this object held. 362 * 363 * @return A flag whether a permit could be acquired. 364 */ 365 private boolean acquirePermit() { 366 if (getLimit() <= NO_LIMIT || acquireCount < getLimit()) { 367 acquireCount++; 368 return true; 369 } 370 return false; 371 } 372 373 /** 374 * The current time period is finished. This method is called by the timer used internally to monitor the time period. It resets the counter and releases 375 * the threads waiting for this barrier. 376 */ 377 synchronized void endOfPeriod() { 378 lastCallsPerPeriod = acquireCount; 379 totalAcquireCount += acquireCount; 380 periodCount++; 381 acquireCount = 0; 382 notifyAll(); 383 } 384 385 /** 386 * Gets the number of invocations of the {@link #acquire()} method for the current period. This may be useful for testing or debugging purposes. 387 * 388 * @return The current number of {@link #acquire()} invocations. 389 */ 390 public synchronized int getAcquireCount() { 391 return acquireCount; 392 } 393 394 /** 395 * Gets the number of calls to the {@link #acquire()} method that can still be performed in the current period without blocking. This method can give an 396 * indication whether it is safe to call the {@link #acquire()} method without risking to be suspended. However, there is no guarantee that a subsequent 397 * call to {@link #acquire()} actually is not-blocking because in the meantime other threads may have invoked the semaphore. 398 * 399 * @return The current number of available {@link #acquire()} calls in the current period. 400 */ 401 public synchronized int getAvailablePermits() { 402 return getLimit() - getAcquireCount(); 403 } 404 405 /** 406 * Gets the average number of successful (i.e. non-blocking) {@link #acquire()} invocations for the entire life-time of this {@code 407 * TimedSemaphore}. This method can be used for instance for statistical calculations. 408 * 409 * @return The average number of {@link #acquire()} invocations per time unit. 410 */ 411 public synchronized double getAverageCallsPerPeriod() { 412 return periodCount == 0 ? 0 : (double) totalAcquireCount / (double) periodCount; 413 } 414 415 /** 416 * Gets the executor service used by this instance. 417 * 418 * @return The executor service. 419 */ 420 protected ScheduledExecutorService getExecutorService() { 421 return executorService; 422 } 423 424 /** 425 * Gets the number of (successful) acquire invocations during the last period. This is the number of times the {@link #acquire()} method was called without 426 * blocking. This can be useful for testing or debugging purposes or to determine a meaningful threshold value. If a limit is set, the value returned by 427 * this method won't be greater than this limit. 428 * 429 * @return The number of non-blocking invocations of the {@link #acquire()} method. 430 */ 431 public synchronized int getLastAcquiresPerPeriod() { 432 return lastCallsPerPeriod; 433 } 434 435 /** 436 * Gets the limit enforced by this semaphore. The limit determines how many invocations of {@link #acquire()} are allowed within the monitored period. 437 * 438 * @return The limit. 439 */ 440 public final synchronized int getLimit() { 441 return limit; 442 } 443 444 /** 445 * Gets the time period. This is the time monitored by this semaphore. Only a given number of invocations of the {@link #acquire()} method is possible in 446 * this period. 447 * 448 * @return The time period. 449 */ 450 public long getPeriod() { 451 return period; 452 } 453 454 /** 455 * Gets the time unit. This is the unit used by {@link #getPeriod()}. 456 * 457 * @return The time unit. 458 */ 459 public TimeUnit getUnit() { 460 return unit; 461 } 462 463 /** 464 * Tests whether the {@link #shutdown()} method has been called on this object. If this method returns <strong>true</strong>, this instance cannot be used 465 * any longer. 466 * 467 * @return A flag whether a shutdown has been performed. 468 */ 469 public synchronized boolean isShutdown() { 470 return shutdown; 471 } 472 473 /** 474 * Prepares an acquire operation. Checks for the current state and starts the internal timer if necessary. This method must be called with the lock of this 475 * object held. 476 */ 477 private void prepareAcquire() { 478 if (isShutdown()) { 479 throw new IllegalStateException("TimedSemaphore is shut down."); 480 } 481 if (task == null) { 482 task = startTimer(); 483 } 484 } 485 486 /** 487 * Sets the limit. This is the number of times the {@link #acquire()} method can be called within the time period specified. If this limit is reached, 488 * further invocations of {@link #acquire()} will block. Setting the limit to a value <= {@link #NO_LIMIT} will cause the limit to be disabled, i.e. an 489 * arbitrary number of{@link #acquire()} invocations is allowed in the time period. 490 * 491 * @param limit The limit. 492 */ 493 public final synchronized void setLimit(final int limit) { 494 this.limit = limit; 495 } 496 497 /** 498 * Initializes a shutdown. After that the object cannot be used anymore. This method can be invoked an arbitrary number of times. All invocations after the 499 * first one do not have any effect. 500 */ 501 public synchronized void shutdown() { 502 if (!shutdown) { 503 if (ownExecutor) { 504 // if the executor was created by this instance, it has 505 // to be shutdown 506 getExecutorService().shutdownNow(); 507 } 508 if (task != null) { 509 task.cancel(false); 510 } 511 shutdown = true; 512 notifyAll(); 513 } 514 } 515 516 /** 517 * Starts the timer. This method is called when {@link #acquire()} is called for the first time. It schedules a task to be executed at fixed rate to monitor 518 * the time period specified. 519 * 520 * @return A future object representing the task scheduled. 521 */ 522 protected ScheduledFuture<?> startTimer() { 523 return getExecutorService().scheduleAtFixedRate(this::endOfPeriod, getPeriod(), getPeriod(), getUnit()); 524 } 525 526 /** 527 * Tries to acquire a permit from this semaphore. If the limit of this semaphore has not yet been reached, a permit is acquired, and this method returns 528 * <strong>true</strong>. Otherwise, this method returns immediately with the result <strong>false</strong>. 529 * 530 * <p> 531 * This method does not honor the fairness setting: it may acquire a permit ahead of waiting callers. It does not test or clear the calling thread's 532 * interrupted status. 533 * </p> 534 * 535 * @return <strong>true</strong> if a permit could be acquired; <strong>false</strong> otherwise. 536 * @throws IllegalStateException Thrown if this semaphore is already shut down. 537 * @since 3.5 538 */ 539 public synchronized boolean tryAcquire() { 540 prepareAcquire(); 541 return acquirePermit(); 542 } 543}