
com.alipay.sofa.jraft.util.Recyclers Maven / Gradle / Ivy
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.alipay.sofa.jraft.util;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Light-weight object pool based on a thread-local stack.
*
* Forked from Netty.
*
* @param the type of the pooled object
*/
public abstract class Recyclers {
private static final Logger LOG = LoggerFactory.getLogger(Recyclers.class);
private static final AtomicInteger idGenerator = new AtomicInteger(Integer.MIN_VALUE);
private static final int OWN_THREAD_ID = idGenerator.getAndIncrement();
private static final int DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD = 4 * 1024; // Use 4k instances as default.
private static final int DEFAULT_MAX_CAPACITY_PER_THREAD;
private static final int INITIAL_CAPACITY;
static {
int maxCapacityPerThread = SystemPropertyUtil.getInt("jraft.recyclers.maxCapacityPerThread", DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD);
if (maxCapacityPerThread < 0) {
maxCapacityPerThread = DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD;
}
DEFAULT_MAX_CAPACITY_PER_THREAD = maxCapacityPerThread;
if (LOG.isDebugEnabled()) {
if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) {
LOG.debug("-Djraft.recyclers.maxCapacityPerThread: disabled");
} else {
LOG.debug("-Djraft.recyclers.maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD);
}
}
INITIAL_CAPACITY = Math.min(DEFAULT_MAX_CAPACITY_PER_THREAD, 256);
}
public static final Handle NOOP_HANDLE = new Handle() {};
private final int maxCapacityPerThread;
private final ThreadLocal> threadLocal = new ThreadLocal>() {
@Override
protected Stack initialValue() {
return new Stack<>(Recyclers.this, Thread.currentThread(), maxCapacityPerThread);
}
};
protected Recyclers() {
this(DEFAULT_MAX_CAPACITY_PER_THREAD);
}
protected Recyclers(int maxCapacityPerThread) {
this.maxCapacityPerThread = Math.max(0, maxCapacityPerThread);
}
@SuppressWarnings("unchecked")
public final T get() {
if (maxCapacityPerThread == 0) {
return newObject(NOOP_HANDLE);
}
Stack stack = threadLocal.get();
DefaultHandle handle = stack.pop();
if (handle == null) {
handle = stack.newHandle();
handle.value = newObject(handle);
}
return (T) handle.value;
}
public final boolean recycle(T o, Handle handle) {
if (handle == NOOP_HANDLE) {
return false;
}
DefaultHandle h = (DefaultHandle) handle;
final Stack> stack = h.stack;
if (h.lastRecycledId != h.recycleId || stack == null) {
throw new IllegalStateException("recycled already");
}
if (stack.parent != this) {
return false;
}
if (o != h.value) {
throw new IllegalArgumentException("o does not belong to handle");
}
h.recycle();
return true;
}
protected abstract T newObject(Handle handle);
public final int threadLocalCapacity() {
return threadLocal.get().elements.length;
}
public final int threadLocalSize() {
return threadLocal.get().size;
}
public interface Handle {}
static final class DefaultHandle implements Handle {
private int lastRecycledId;
private int recycleId;
private Stack> stack;
private Object value;
DefaultHandle(Stack> stack) {
this.stack = stack;
}
public void recycle() {
Thread thread = Thread.currentThread();
final Stack> stack = this.stack;
if (lastRecycledId != recycleId || stack == null) {
throw new IllegalStateException("recycled already");
}
if (thread == stack.thread) {
stack.push(this);
return;
}
// we don't want to have a ref to the queue as the value in our weak map
// so we null it out; to ensure there are no races with restoring it later
// we impose a memory ordering here (no-op on x86)
Map, WeakOrderQueue> delayedRecycled = Recyclers.delayedRecycled.get();
WeakOrderQueue queue = delayedRecycled.get(stack);
if (queue == null) {
delayedRecycled.put(stack, queue = new WeakOrderQueue(stack, thread));
}
queue.add(this);
}
}
private static final ThreadLocal
© 2015 - 2025 Weber Informatics LLC | Privacy Policy