netty-recycler,带你从设计层面完全了解它
我在初学recycler的时候看了不少文章,这些文章基本都写得不错,介绍的非常详细,recycler里面的组件也基本都介绍了,但是我在看的时候一直在想他引入这么多组件,设计这么复杂是为了什么,后面我终于搞懂了,所以分享给大家~
v1版本的recycler
import java.util.LinkedList;
import java.util.Stack;
public abstract class RecyclerV1<T> {
private LinkedList<T> stack = new LinkedList();
// private Stack<T> stack = new Stack();
public final T get() {
if(stack.isEmpty()){
return newObject();
}
return stack.pop();
}
public final void recycle(T object) {
stack.push(object);
}
protected abstract T newObject();
}
public class User {
private RecyclerV1 recyclerV1;
public User(RecyclerV1 recyclerV1){
this.recyclerV1 = recyclerV1;
}
public void recycle() {
recyclerV1.recycle(this);
}
}
import org.junit.jupiter.api.Test;
public class TestRecyclerV1 {
private static RecyclerV1<User> newRecycler() {
return new RecyclerV1<User>() {
@Override
protected User newObject() {
return new User(this);
}
};
}
public static void main(String[] args) {
RecyclerV1<User> recycler = newRecycler();
User user = recycler.get();
user.recycle();
User user2 = recycler.get();
System.out.println(user==user2);
}
/**
* 多线程get可能会错误, 线程不安全
*/
@Test
public void testGetError(){
RecyclerV1<User> recycler = newRecycler();
User user = recycler.get();
user.recycle();
for(int i=0;i<10;i++){
new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
recycler.get();
}).start();
}
}
@Test
public void testRepeatRecycler() {
RecyclerV1<User> recycler = newRecycler();
User user = recycler.get();
//同一个对象不能回收多次
user.recycle();
user.recycle();
}
}
这个版本的recycler有几个严重的bug:
- 其一是RecyclerV1#get函数,由于线程竞争,可能导致pop产生NoSuchElementException异常。
- 其二是RecyclerV1#recycle函数,同一个对象可以回收多次。
下面我们将逐渐修复这些bug
v2版本的recycler
在这个版本我将修复重复回收对象的问题,要想解决这个问题,我们就得知道对象有没有被回收,如果已经被回收了,我们就报一个错误recycled already。我们需要对对象包装一层。即用DefaultHandle包装对象。并且记录一个recyclerId标记回收对象的线程是谁。代码如下:
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public abstract class RecyclerV2<T> {
private LinkedList<DefaultHandle> stack = new LinkedList();
// private Stack<T> stack = new Stack();
public final T get() {
if(stack.isEmpty()){
DefaultHandle<T> handle = new DefaultHandle(stack);
T value = newObject(handle);
handle.value = value;
return value;
}
return (T) stack.pop().value;
}
protected abstract T newObject(Handle<T> handle);
public interface Handle<T> {
void recycle(T self);
}
private static final class DefaultHandle<T> implements Handle<T>{
T value;
volatile long recyclerId;
private LinkedList<DefaultHandle> stack;
public DefaultHandle(LinkedList<DefaultHandle> stack){
this.stack = stack;
}
private static final AtomicLongFieldUpdater<DefaultHandle<?>> RECYCLER_ID_UPDATER;
static {
AtomicLongFieldUpdater<?> updater = AtomicLongFieldUpdater.newUpdater(
DefaultHandle.class, "recyclerId");
RECYCLER_ID_UPDATER = (AtomicLongFieldUpdater<DefaultHandle<?>>) updater;
}
@Override
public final void recycle(T object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
if(recyclerId!=0){
throw new IllegalStateException("recycled already");
}
if(!RECYCLER_ID_UPDATER.compareAndSet(this,0,Thread.currentThread().getId())){
throw new IllegalStateException("recycled already");
}
stack.push(this);
}
}
}
public class User {
private RecyclerV2.Handle handle;
public User(RecyclerV2.Handle handle){
this.handle = handle;
}
public void recycle() {
handle.recycle(this);
}
}
import org.junit.jupiter.api.Test;
public class TestRecyclerV2 {
private static RecyclerV2<User> newRecycler() {
return new RecyclerV2<User>() {
@Override
protected User newObject(RecyclerV2.Handle handle) {
return new User(handle);
}
};
}
public static void main(String[] args) {
RecyclerV2<User> recycler = newRecycler();
User user = recycler.get();
user.recycle();
User user2 = recycler.get();
System.out.println(user==user2);
}
/**
* 多线程get可能会错误, 线程不安全
*/
@Test
public void testGetError(){
RecyclerV2<User> recycler = newRecycler();
User user = recycler.get();
user.recycle();
for(int i=0;i<10;i++){
new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
recycler.get();
}).start();
}
}
@Test
public void testRepeatRecycler() {
RecyclerV2<User> recycler = newRecycler();
User user = recycler.get();
//同一个对象不能回收多次
user.recycle();
user.recycle();
}
}
在这个版本里我把recycle方法放到了新建的内部类DefaultHandle里面,并且对recycleId做了volatile和cas处理来保证回收对象的线程安全特性。但是我们依然没有解决get函数由于线程竞争导致的pop NoSuchElementE xception, 其实我们把LinkedList换成线程安全的Stack即可解决。
tips: stack内部采用Syncronzied,效率较低,netty没有采用这种办法,而是采用了ThreadLocal来解决这个问题。
v3版本的recycler
v2版本我们解决了对象重复回收的bug,v3版本我们来思考下如何解决get函数由于线程竞争导致的pop NoSuchElementException。改动代码如下:
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public abstract class RecyclerV3<T> {
private ThreadLocal<LinkedList<DefaultHandle>> threadLocal = new ThreadLocal<LinkedList<DefaultHandle>>(){
@Override
protected LinkedList<DefaultHandle> initialValue() {
return new LinkedList();
}
};
public final T get() {
LinkedList<DefaultHandle> stack = threadLocal.get();
if(stack.isEmpty()){
DefaultHandle<T> handle = new DefaultHandle(stack);
T value = newObject(handle);
handle.value = value;
return value;
}
return (T) stack.pop().value;
}
protected abstract T newObject(Handle<T> handle);
public int size(){
return threadLocal.get().size();
}
public interface Handle<T> {
void recycle(T self);
}
private static final class DefaultHandle<T> implements Handle<T>{
T value;
volatile long recyclerId;
private LinkedList<DefaultHandle> stack;
public DefaultHandle(LinkedList<DefaultHandle> stack){
this.stack = stack;
}
private static final AtomicLongFieldUpdater<DefaultHandle<?>> RECYCLER_ID_UPDATER;
static {
AtomicLongFieldUpdater<?> updater = AtomicLongFieldUpdater.newUpdater(
DefaultHandle.class, "recyclerId");
RECYCLER_ID_UPDATER = (AtomicLongFieldUpdater<DefaultHandle<?>>) updater;
}
@Override
public final void recycle(T object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
if(recyclerId!=0){
throw new IllegalStateException("recycled already");
}
if(!RECYCLER_ID_UPDATER.compareAndSet(this,0,Thread.currentThread().getId())){
throw new IllegalStateException("recycled already");
}
stack.push(this);
}
}
}
在v3版本的代码里,我为每个线程分配了一个stack,这样自然就避免了get函数线程竞争,也就解决get函数的bug。但是这样处理带来了一个问题,注意看DefalultHandle的stack属性,它其实是创建线程的stack,当不同线程调用recycle函数时,他们都是使用stack来push,这里会有竞争,即非线程安全。
@Test
public void testRecycleByOtherThread() throws IOException, InterruptedException {
RecyclerV3<User> recycler = newRecycler();
List<User> users = new ArrayList<>();
for(int i=0;i<10;i++){
User user = recycler.get();//main线程创建的对象
users.add(user);
}
for (User user : users) {
new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
user.recycle();
}).start();
}
Thread.sleep(1000);
System.out.println(recycler.size());
}
执行结果如下:
v4版本的recycler
v3版本我们解决了get函数的竞争,但是导致了recycle函数的竞争,下面我们不妨删除DefalultHandle的stack,stack统一从threadlocal中拿。
public abstract class RecyclerV4<T> {
private ThreadLocal<LinkedList<DefaultHandle>> threadLocal = new ThreadLocal<LinkedList<DefaultHandle>>(){
@Override
protected LinkedList<DefaultHandle> initialValue() {
return new LinkedList();
}
};
public final T get() {
LinkedList<DefaultHandle> stack = threadLocal.get();
if(stack.isEmpty()){
DefaultHandle<T> handle = new DefaultHandle();
T value = newObject(handle);
handle.value = value;
return value;
}
return (T) stack.pop().value;
}
protected abstract T newObject(Handle<T> handle);
public interface Handle<T> {
void recycle(T self);
}
private final class DefaultHandle<T> implements Handle<T>{
T value;
volatile long recyclerId;//记录回收value的线程id
private final AtomicLongFieldUpdater<DefaultHandle<?>> RECYCLER_ID_UPDATER;
{
AtomicLongFieldUpdater<?> updater = AtomicLongFieldUpdater.newUpdater(
DefaultHandle.class, "recyclerId");
RECYCLER_ID_UPDATER = (AtomicLongFieldUpdater<DefaultHandle<?>>) updater;
}
@Override
public final void recycle(T object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
if(recyclerId!=0){
throw new IllegalStateException("recycled already");
}
if(!RECYCLER_ID_UPDATER.compareAndSet(this,0,Thread.currentThread().getId())){
throw new IllegalStateException("recycled already");
}
LinkedList<DefaultHandle> stack = threadLocal.get();
stack.push(this);
}
}
}
在代码里,我为每个线程分配了一个stack,这样自然就避免了get和recycle函数线程竞争,但是这样处理带来了一个更大的问题,由于之前所有线程共享一个stack,不管哪个线程过来回收都是回收到这个共享stack中。但我们现在为每个线程分配了一个stack,问题就变得有趣起来了。让我们来分析下面这个例子:
@Test
public void testRecycleByOtherThread() throws InterruptedException {
RecyclerV3<User> recycler = newRecycler();
User user = recycler.get();//main线程创建的对象
Thread t = new Thread(()->{
user.recycle();//由线程t回收到自己的stack
});
t.start();
t.join();
User user2 = recycler.get();
System.out.println(user == user2);//始终是false
}
现在每个线程都有一个stack,main线程创建的线程被t线程回收到t线程的stack去了,所以后面main线程再来get得到的对象又是一个新创建的对象。
tips: 上面这样是不符合我们的预期的,我们希望不管回收对象的线程是谁,哪个线程创建的对象最终也会被回收这个线程去。像上面这种情况,t线程把对象回收到自己的stack中但是线程立马就死了,那这个对象相当于被浪费了。
v5版本的recycler
在v4版本中我们解决了v1提出的两个bug,但是引出了一个更大bug:线程main创建的对象没有回收到main线程的stack。从v3版本和v4版本回收对象的时候可以看出:既不能回收到共享stack,也不能回收到各自线程的stack。
如何解决这个问题?
我们可以把存储对象的容器分开来,stack存储创建线程回收的对象,otherStack存储其他线程帮助创建线程回收的对象。回收的时候判断当前线程是不是创建线程,是的话直接回收到stack中,不是的话回收到otherStack(注意每个回收线程对应一个otherStack,所以不会有竞争)。然后get的时候再从otherStack收割对象到stack中。
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public abstract class RecyclerV5<T> {
private ThreadLocal<Stack<T>> threadLocal = new ThreadLocal<Stack<T>>(){
@Override
protected Stack<T> initialValue() {
return new Stack<>();
}
};
public final T get() {
Stack<T> stack = threadLocal.get();
DefaultHandle<T> handle = stack.pop();
if(null==handle){
handle = new DefaultHandle(stack);
T value = newObject(handle);
handle.value = value;
}
return handle.value;
}
protected abstract T newObject(Handle<T> handle);
private final class Stack<T>{
//创建线程的对象栈
LinkedList<DefaultHandle<T>> stack = new LinkedList<>();
//创建线程
final WeakReference<Thread> threadRef;
//其他线程帮创建线程回收的对象
Map<Thread,LinkedList<DefaultHandle<T>>> otherStackMap = new WeakHashMap<>();
public Stack(){
threadRef = new WeakReference(Thread.currentThread());
}
public boolean isEmpty(){
return stack.isEmpty();
}
public DefaultHandle<T> pop(){
if(isEmpty()){
scavenge();
}
if(isEmpty()){
return null;
}
return stack.pop();
}
public void scavenge(){
//todo bug
for (LinkedList<DefaultHandle<T>> value : otherStackMap.values()) {
stack.addAll(value);
}
}
public void push(DefaultHandle<T> handle){
//当前回收线程是谁
Thread recycleThread = Thread.currentThread();
//创建线程已死则不需要回收了
if(null==threadRef.get()){
return;
}
//创建线程和回收线程是同一个
if (threadRef.get() == recycleThread) {
pushNow(handle);
}else{
pushLater(handle);
}
}
private void pushNow(DefaultHandle<T> handle){
stack.push(handle);
}
/**
* 其他线程帮助回收 创建线程创建的对象,涉及到多线程竞争
* @param handle
*/
private void pushLater(DefaultHandle<T> handle){
Thread recycleThread = Thread.currentThread();
LinkedList<DefaultHandle<T>> stack = otherStackMap.getOrDefault(recycleThread,new LinkedList<>());
stack.push(handle);
otherStackMap.put(recycleThread,stack);
}
}
public interface Handle<T> {
void recycle(T self);
}
private final class DefaultHandle<T> implements Handle<T>{
T value;
volatile long recyclerId;//记录回收value的线程id
private Stack<T> stack;
public DefaultHandle(Stack<T> stack) {
this.stack = stack;
}
private final AtomicLongFieldUpdater<DefaultHandle<?>> RECYCLER_ID_UPDATER;
{
AtomicLongFieldUpdater<?> updater = AtomicLongFieldUpdater.newUpdater(
DefaultHandle.class, "recyclerId");
RECYCLER_ID_UPDATER = (AtomicLongFieldUpdater<DefaultHandle<?>>) updater;
}
@Override
public final void recycle(T object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
if(recyclerId!=0){
throw new IllegalStateException("recycled already");
}
if(!RECYCLER_ID_UPDATER.compareAndSet(this,0,Thread.currentThread().getId())){
throw new IllegalStateException("recycled already");
}
stack.push(this);
}
}
}
public class User {
private RecyclerV5.Handle handle;
public User(RecyclerV5.Handle handle){
this.handle = handle;
}
public void recycle() {
handle.recycle(this);
}
}
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
public class TestRecyclerV5 {
private static RecyclerV5<User> newRecycler() {
return new RecyclerV5<User>() {
@Override
protected User newObject(Handle handle) {
return new User(handle);
}
};
}
private void t(User user) throws InterruptedException {
Thread t = new Thread(()->{
user.recycle();//由线程a回收到自己的stack
});
t.start();
t.join();
}
@Test
public void testRecycleByOtherThread() throws InterruptedException {
RecyclerV5<User> recycler = newRecycler();
User user = recycler.get();//main线程创建的对象
t(user);
System.gc();//gc 会清除otherStack,这行注释掉下面就是true了
User user2 = recycler.get();
System.out.println(user == user2);//始终是false
}
}
在这个版本的代码中我引入了自己的Stack组件,储存了stack,otherStackMap以及创建线程threadRef。push的时候我会判断回收线程是否是创建线程是的话回收到stack,不是的话回收到otherStackMap。pop的时候如果stack是空,则先从otherStackMap收割对象。
这个版本的代码有个bug,scavenge函数是有问题的,试想当创建线程在scavenge遍历otherStackMap时,其他回收线程也可能往这个map里put元素,导致ConcurrentModifyException。在netty源码中又引入了WeakOrderQueue和Link组件去解决这个问题。
v6版本的recycler
这个版本我引入了WeakOrderQueue和Link组件来解决v5版本遗留的bug。
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public abstract class RecyclerV6<T> {
private ThreadLocal<Stack<T>> threadLocal = new ThreadLocal<Stack<T>>(){
@Override
protected Stack<T> initialValue() {
return new Stack<>();
}
};
private static final int LINK_CAPACITY = 16;//16
private static final ThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
new ThreadLocal<Map<Stack<?>, WeakOrderQueue>>() {
@Override
protected Map<Stack<?>, WeakOrderQueue> initialValue() {
return new WeakHashMap<Stack<?>, WeakOrderQueue>();
}
};
public final T get() {
Stack<T> stack = threadLocal.get();
DefaultHandle<T> handle = stack.pop();
if(null==handle){
handle = new DefaultHandle(stack);
T value = newObject(handle);
handle.value = value;
}
return handle.value;
}
protected abstract T newObject(Handle<T> handle);
private static final class WeakOrderQueue extends WeakReference<Thread> {
private final Link head;
private Link tail;
private WeakOrderQueue next;
private WeakOrderQueue() {
super(null);
head = new Link();
}
private WeakOrderQueue(Stack<?> stack, Thread thread) {
super(thread);
head = new Link();
tail = head;
}
public static <T> WeakOrderQueue newQueue(Stack stack, Thread recycleThread) {
final WeakOrderQueue queue = new WeakOrderQueue(stack, recycleThread);
stack.setHead(queue);
return queue;
}
public <T> void add(DefaultHandle<T> handle) {
Link tail = this.tail;
int writeIndex;
//最后一个link写满了,新建link
if ((writeIndex = tail.get()) == LINK_CAPACITY) {
Link link = new Link();
// We allocate a Link so reserve the space
this.tail = tail = tail.next = link;
writeIndex = tail.get();
}
tail.elements[writeIndex] = handle;
handle.stack = null;
// we lazy set to ensure that setting stack to null appears before we unnull it in the owning thread;
// this also means we guarantee visibility of an element in the queue if we see the index updated
tail.lazySet(writeIndex + 1);
}
public void setNext(WeakOrderQueue next) {
this.next = next;
}
public <T> boolean transfer(Stack<T> stack) {
final int srcStart = head.readIndex;
int srcEnd = head.get();
final int srcSize = srcEnd - srcStart;
if (srcSize == 0) {
return false;
}
final DefaultHandle[] srcElems = head.elements;
for (int i = srcStart; i < srcEnd; i++) {
DefaultHandle<T> element = srcElems[i];
srcElems[i] = null;
stack.pushNow(element);
}
head.readIndex = srcEnd;
return true;
}
public WeakOrderQueue getNext() {
return next;
}
final class Link extends AtomicInteger {
final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];
int readIndex;
Link next;
}
}
private static final class Stack<T>{
//创建线程的对象栈
LinkedList<DefaultHandle<T>> stack = new LinkedList<>();
private volatile WeakOrderQueue head;
private WeakOrderQueue cursor,prev;
//创建线程
final WeakReference<Thread> threadRef;
public Stack(){
threadRef = new WeakReference(Thread.currentThread());
}
public boolean isEmpty(){
return stack.isEmpty();
}
public DefaultHandle<T> pop(){
if(isEmpty()){
scavenge();
}
if(isEmpty()){
return null;
}
return stack.pop();
}
private boolean scavenge() {
// continue an existing scavenge, if any
if (scavengeSome()) {
return true;
}
// reset our scavenge cursor
prev = null;
cursor = head;
return false;
}
public boolean scavengeSome(){
WeakOrderQueue cursor = this.cursor;
WeakOrderQueue prev;
if(null==cursor){
prev = null;
cursor = head;
if (cursor == null) {
return false;
}
}else{
prev = this.prev;
}
boolean success = false;
do {
//当前游标的WeakOrderQueue收割成功直接return
if (cursor.transfer(this)) {
success = true;
break;
}
WeakOrderQueue next = cursor.getNext();
//回收线程已死
if (cursor.get() == null) {
// If the thread associated with the queue is gone, unlink it, after
// performing a volatile read to confirm there is no data left to collect.
// We never unlink the first queue, as we don't want to synchronize on updating the head.
for (;;) {
if (cursor.transfer(this)) {
success = true;
} else {
break;
}
}
if (prev != null) {
// prev!=null说明cursor不指向头节点,因此要将cursor指向的节点移出链表
prev.setNext(next);
}
} else {
prev = cursor;
}
cursor = next;
} while (cursor != null && !success);
this.prev = prev;
this.cursor = cursor;
return success;
}
public void push(DefaultHandle<T> handle){
//当前回收线程是谁
Thread recycleThread = Thread.currentThread();
//创建线程已死则不需要回收了
if(null==threadRef.get()){
return;
}
//创建线程和回收线程是同一个
if (threadRef.get() == recycleThread) {
pushNow(handle);
}else{
pushLater(handle);
}
}
public void pushNow(DefaultHandle<T> handle){
stack.push(handle);
}
/**
* 其他线程帮助回收 创建线程创建的对象,涉及到多线程竞争
* @param handle
*/
private void pushLater(DefaultHandle<T> handle){
Thread recycleThread = Thread.currentThread();
//创建线程的栈,某个回收线程对应的节点
Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
//某个回收线程对应的节点
WeakOrderQueue queue = delayedRecycled.get(this);
if(null==queue){
if ((queue = newWeakOrderQueue(recycleThread)) == null) {
return;
}
delayedRecycled.put(this, queue);
}
queue.add(handle);
}
private WeakOrderQueue newWeakOrderQueue(Thread recycleThread) {
return WeakOrderQueue.newQueue(this, recycleThread);
}
synchronized void setHead(WeakOrderQueue queue) {
queue.setNext(head);
head = queue;
}
}
public interface Handle<T> {
void recycle(T self);
}
private static final class DefaultHandle<T> implements Handle<T>{
T value;
volatile long recyclerId;//记录回收value的线程id
private Stack<T> stack;
public DefaultHandle(Stack<T> stack) {
this.stack = stack;
}
private final AtomicLongFieldUpdater<DefaultHandle<?>> RECYCLER_ID_UPDATER;
{
AtomicLongFieldUpdater<?> updater = AtomicLongFieldUpdater.newUpdater(
DefaultHandle.class, "recyclerId");
RECYCLER_ID_UPDATER = (AtomicLongFieldUpdater<DefaultHandle<?>>) updater;
}
@Override
public final void recycle(T object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
if(recyclerId!=0){
throw new IllegalStateException("recycled already");
}
if(!RECYCLER_ID_UPDATER.compareAndSet(this,0,Thread.currentThread().getId())){
throw new IllegalStateException("recycled already");
}
stack.push(this);
}
}
}
我迭代了6个版本说明了Recycler的设计思想以及引入这么多组件的目的,Recycler是一个非常轻量级的对象池,他希望不加任何锁实现它。关于为什么引入DefaultHandle,Stack,WeakOrderQueue,Link,相信您只要认真阅读过并且敲过这6个版本的代码,一定非常理解了。