All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juli.logging.com.fasterxml.jackson.core.util.RecyclerPool Maven / Gradle / Ivy

There is a newer version: 10.1.24
Show newest version
package org.apache.juli.logging.com.fasterxml.jackson.core.util;

import java.io.Serializable;
import java.util.Deque;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicReference;

/**
 * API for object pools that control creation and possible reuse of
 * objects that are costly to create (often things like encoding/decoding buffers).
 *

* Also contains partial (base) implementations for pools that use different * strategies on retaining objects for reuse. * Following implementations are included: *

    *
  • {@link NonRecyclingPoolBase} which does not retain or recycle anything and * will always simply construct and return new instance when * {@code acquireBufferRecycler} is called *
  • *
  • {@link ThreadLocalPoolBase} which uses {@link ThreadLocal} to retain at most * 1 object per {@link Thread}. *
  • *
  • {@link BoundedPoolBase} is "bounded pool" and retains at most N objects (default value being * {@link BoundedPoolBase#DEFAULT_CAPACITY}) at any given time. *
  • *
  • Two implementations -- {@link ConcurrentDequePoolBase}, {@link LockFreePoolBase} * -- are "unbounded" and retain any number of objects released: in practice * it is at most the highest number of concurrently used {@link BufferRecycler}s. *
  • *
* *

* Default implementations are also included as nested classes. * * @param

Type of Objects pool recycles * * @since 2.16 */ public interface RecyclerPool

> extends Serializable { /** * Simple add-on interface that poolable entities must implement. * * @param

Self type */ public interface WithPool

> { /** * Method to call to add link from pooled item back to pool * that handles it * * @param pool Pool that "owns" pooled item * * @return This item (for call chaining) */ P withPool(RecyclerPool

pool); /** * Method called when this item is to be released back to the * pool that owns it (if any) */ void releaseToPool(); } /** * Method called to acquire a Pooled value from this pool * AND make sure it is linked back to this * {@link RecyclerPool} as necessary for it to be * released (see {@link #releasePooled}) later after usage ends. * Actual acquisition is done by a call to {@link #acquirePooled()}. *

* Default implementation calls {@link #acquirePooled()} followed by * a call to {@link WithPool#withPool}. * * @return Pooled instance for caller to use; caller expected * to call {@link #releasePooled} after it is done using instance. */ default P acquireAndLinkPooled() { return acquirePooled().withPool(this); } /** * Method for sub-classes to implement for actual acquire logic; called * by {@link #acquireAndLinkPooled()}. * * @return Instance acquired (pooled or just constructed) */ P acquirePooled(); /** * Method that should be called when previously acquired (see {@link #acquireAndLinkPooled}) * pooled value that is no longer needed; this lets pool to take ownership * for possible reuse. * * @param pooled Pooled instance to release back to pool */ void releasePooled(P pooled); /* /********************************************************************** /* Partial/base RecyclerPool implementations /********************************************************************** */ /** * Default {@link RecyclerPool} implementation that uses * {@link ThreadLocal} for recycling instances. * Instances are stored using {@link java.lang.ref.SoftReference}s so that * they may be Garbage Collected as needed by JVM. *

* Note that this implementation may not work well on platforms where * {@link java.lang.ref.SoftReference}s are not well supported (like * Android), or on platforms where {@link java.lang.Thread}s are not * long-living or reused (like Project Loom). */ abstract class ThreadLocalPoolBase

> implements RecyclerPool

{ private static final long serialVersionUID = 1L; protected ThreadLocalPoolBase() { } // // // Actual API implementation @Override public P acquireAndLinkPooled() { // since this pool doesn't do anything on release it doesn't need to be registered on the BufferRecycler return acquirePooled(); } @Override public abstract P acquirePooled(); @Override public void releasePooled(P pooled) { ; // nothing to do, relies on ThreadLocal } } /** * {@link RecyclerPool} implementation that does not use * any pool but simply creates new instances when necessary. */ abstract class NonRecyclingPoolBase

> implements RecyclerPool

{ private static final long serialVersionUID = 1L; // // // Actual API implementation @Override public P acquireAndLinkPooled() { // since this pool doesn't do anything on release it doesn't need to be registered on the BufferRecycler return acquirePooled(); } @Override public abstract P acquirePooled(); @Override public void releasePooled(P pooled) { ; // nothing to do, there is no underlying pool } } /** * Intermediate base class for instances that are stateful and require * special handling with respect to JDK serialization, to retain * "global" reference distinct from non-shared ones. */ abstract class StatefulImplBase

> implements RecyclerPool

{ private static final long serialVersionUID = 1L; public final static int SERIALIZATION_SHARED = -1; public final static int SERIALIZATION_NON_SHARED = 1; /** * Value that indicates basic aspects of pool for JDK serialization; * either marker for shared/non-shared, or possibly bounded size; * depends on sub-class. */ protected final int _serialization; protected StatefulImplBase(int serialization) { _serialization = serialization; } protected Optional> _resolveToShared(StatefulImplBase

shared) { if (_serialization == SERIALIZATION_SHARED) { return Optional.of(shared); } return Optional.empty(); } public abstract P createPooled(); } /** * {@link RecyclerPool} implementation that uses * {@link ConcurrentLinkedDeque} for recycling instances. *

* Pool is unbounded: see {@link RecyclerPool} what this means. */ abstract class ConcurrentDequePoolBase

> extends StatefulImplBase

{ private static final long serialVersionUID = 1L; protected final transient Deque

pool; protected ConcurrentDequePoolBase(int serialization) { super(serialization); pool = new ConcurrentLinkedDeque<>(); } // // // Actual API implementation @Override public P acquirePooled() { P pooled = pool.pollFirst(); if (pooled == null) { pooled = createPooled(); } return pooled; } @Override public void releasePooled(P pooled) { pool.offerLast(pooled); } } /** * {@link RecyclerPool} implementation that uses * a lock free linked list for recycling instances. * Pool is unbounded: see {@link RecyclerPool} for * details on what this means. */ abstract class LockFreePoolBase

> extends StatefulImplBase

{ private static final long serialVersionUID = 1L; // Needs to be transient to avoid JDK serialization from writing it out private final transient AtomicReference> head; // // // Life-cycle (constructors, factory methods) protected LockFreePoolBase(int serialization) { super(serialization); head = new AtomicReference<>(); } // // // Actual API implementation @Override public P acquirePooled() { // This simple lock free algorithm uses an optimistic compareAndSet strategy to // populate the underlying linked list in a thread-safe way. However, under very // heavy contention, the compareAndSet could fail multiple times, so it seems a // reasonable heuristic to limit the number of retries in this situation. for (int i = 0; i < 3; i++) { Node

currentHead = head.get(); if (currentHead == null) { return createPooled(); } if (head.compareAndSet(currentHead, currentHead.next)) { currentHead.next = null; return currentHead.value; } } return createPooled(); } @Override public void releasePooled(P pooled) { Node

newHead = new Node<>(pooled); for (int i = 0; i < 3; i++) { newHead.next = head.get(); if (head.compareAndSet(newHead.next, newHead)) { return; } } } protected static class Node

{ final P value; Node

next; Node(P value) { this.value = value; } } } /** * {@link RecyclerPool} implementation that uses * a bounded queue ({@link ArrayBlockingQueue} for recycling instances. * This is "bounded" pool since it will never hold on to more * pooled instances than its size configuration: * the default size is {@link BoundedPoolBase#DEFAULT_CAPACITY}. */ abstract class BoundedPoolBase

> extends StatefulImplBase

{ private static final long serialVersionUID = 1L; /** * Default capacity which limits number of items that are ever * retained for reuse. */ public final static int DEFAULT_CAPACITY = 100; private final transient ArrayBlockingQueue

pool; private final transient int capacity; // // // Life-cycle (constructors, factory methods) protected BoundedPoolBase(int capacityAsId) { super(capacityAsId); capacity = (capacityAsId <= 0) ? DEFAULT_CAPACITY : capacityAsId; pool = new ArrayBlockingQueue<>(capacity); } // // // Actual API implementation @Override public P acquirePooled() { P pooled = pool.poll(); if (pooled == null) { pooled = createPooled(); } return pooled; } @Override public void releasePooled(P pooled) { pool.offer(pooled); } // // // Other methods public int capacity() { return capacity; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy