ThreadPoolExecutor thread pool "source code analysis"

ThreadPoolExecutor thread pool source code analysis

White teeth say

Long time no update, no reason not to learn, but finished school do not know how to write, as well as a surge of sound in the ears told me that flying public number, write number of people than the public to see the public number, the same topic the article too much ....... But later I figured out, although a similar article a lot, but I did not write it himself finish helps sort out the relevant knowledge, just if we can bring some help, so much the better, so white teeth, or encourage more output, the output Forced input, which is harvested only do we have to know

ThreadPoolExecutor 类图

ThreadPoolExecutor类图

By class diagram shows, ThreadPoolExecutor is a ExecutorService, the task can be performed by a thread pool

Common properties

// 线程池中重要的变量 ctl,类型为 AtomicInteger,一个变量同时记录线程池状态和线程个数
// Integer 的位数为 32 位,其中高 3 位表示线程池的状态,低 29 位表示线程的个数。默认为 RUNNING 状态,线程个数为 0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 线程个数掩码位数,去掉高 3 位代表线程个数的 bit 位
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程最大个数,低 29 位 00011111111111111111111111111111
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
复制代码

Write JDK source code had to admire these chiefs, the use of a variable so well, ctl variable bit operation by the simultaneous expression of the state and the number of thread pool threads associated with the following method facie

Common method

// 计算线程池的状态 ~CAPACITY 为:11100000000000000000000000000000,通过让 ctl 与 ~CAPACITY 相与,相当于取高 3 位的值(前面说了 ctl 高 3 位表示线程池状态)
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 计算线程个数 CAPACITY 为:00011111111111111111111111111111,通过让 ctl 与 CAPACITY 相与,相当于取低 29 位的值(前面说了 ctl 低 29 位表示线程个数)
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 计算 ct l的值,用线程池状态和线程个数进行或运算
private static int ctlOf(int rs, int wc) { return rs | wc; }
复制代码

In the source code often we see these methods are not used very clever?

Thread Pool Lifecycle

// 默认状态,接收新任务并处理阻塞队列里的任务
private static final int RUNNING    = -1 << COUNT_BITS;
// 拒绝新任务但是处理阻塞队列里的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 拒绝新任务并且抛弃阻塞队列里的任务,同时中断正在处理的任务
private static final int STOP       =  1 << COUNT_BITS;
// 所有任务都已经执行完成,线程数是 0,将调用 terminated() 方法
private static final int TIDYING    =  2 << COUNT_BITS;
// 终止状态,调用完 terminated() 方法后的状态
private static final int TERMINATED =  3 << COUNT_BITS;
复制代码

Remark

If you want to see the above variables are binary representation, by methods Integer.toBinaryString (int i) View

State of the thread pool Conversion

RUNNING -> SHUTDOWN:当调用 shutdown() 方法时,也可能隐式的调用 finalize() 方法时(因为 finalize() 方法也是调用的 shutdown() 方法)
   On invocation of shutdown(), perhaps implicitly in finalize()
   
(RUNNING or SHUTDOWN) -> STOP:当调用 shutdownNow() 方法时
   On invocation of shutdownNow()
   
SHUTDOWN -> TIDYING:当队列和线程池都空时
   When both queue and pool are empty
   
STOP -> TIDYING:当线程池为空时
   When pool is empty
   
TIDYING -> TERMINATED:当 terminated() 方法完成时
   When the terminated() hook method has completed
复制代码

Thread pool constructor

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
复制代码

Create a thread pool like the common parameters, the following brief introduction

corePoolSize:线程池中的核心线程数,即使他们处于空闲状态,除非 allowCoreThreadTimeOut 被设置了
maximumPoolSize:线程池中的最大线程数
workQueue:存放还未被执行任务的阻塞队列
threadFactory:创建线程的工厂类
rejectHandle:拒绝策略,当线程个数达到最大线程数,同时任务队列满了。就会执行拒绝策略。拒绝策略有:AbortPolicy(直接抛出异常)、CallerRunsPolicy(调用者所在线程来执行任务)、DiscardOldestPolicy(从任务队列中移除一个待执行的任务(最早提交的),然后再次执行任务)、DiscardPolicy(直接抛弃任务)      
keepAliveTime:存活时间,当线程个数大于了核心线程数,且处于空闲状态,这些空闲线程可存活的最大时间
复制代码

Source dismantling

submit method

When using the thread pool, we usually call ThreadPoolExecutor.submit (task) method, directly to the thread pool to handle the task, and then returned to us a Future, behind the method can obtain task results Future.get ()

public Future<?> submit(Runnable task) {
    // 任务为空,直接抛异常
    if (task == null) throw new NullPointerException();
    // 把任务封装成 RunnableFuture 
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    // 执行封装后的任务
    execute(ftask);
    return ftask;
}
复制代码
newTaskFor way

newTaskFor the task is packaged into a class RunnableFuture can obtain results get method

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

public FutureTask(Runnable runnable, V result) {
    // 这里把 Runnable 适配成 Callable 类型的任务,result 是当任务成功完成时返回的结果,如果需要特殊结  果,就用 null 就行了
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    // 这里通过 RunnableAdapter 适配任务和任务的结果
    return new RunnableAdapter<T>(task, result);
}

// 适配类 RunnableAdapter,这种写法,我们可以借鉴下
static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}
复制代码
execute method

Try to execute the task to the thread pool thread to perform the task may be new, it may be multiplexed thread pool. If the thread pool could not perform the task (there may be two reasons, 1. The thread pool has been closed, 2 thread has reached maximum capacity) will conduct a denial policy

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:留着原汁原味的总结
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    
    // 获取表示线程池状态和线程个数的组合变量 ctl
    int c = ctl.get();
    // 判断线程个数是否小于核心线程数,如果小于就新建一个核心线程来执行任务
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
   // 如果线程池处于 RUNNAING 状态,就把任务添加到阻塞队列中(代码运行到这里说明要么线程个数>=核心线程数,要么执行 addWorder 方法失败)
    if (isRunning(c) && workQueue.offer(command)) {
        // 再次获取组合变量 ctl,做二次检查(因为可能在此之前,线程池的状态已经发生了改变)
        int recheck = ctl.get();
        // 如果线程池状态不是 RNUUAING 状态,就把该任务从阻塞任务队列中移除,并执行拒绝策略
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程池中线程个数为 0,就新建一个线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果阻塞任务队列满了,新建线程,如果创建线程失败(即线程个数达到了最大线程个数),执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

复制代码
summary

When the job is submitted to the thread pool, determining the number of running threads is less than corePoolSize, if it is less than a new thread to handle this task even if other threads are idle.

Then come the task, if there is a free kernel threads, directly perform tasks. If the core threads are busy, then put the task to be performed added to the task queue.

If the task queue is full, and the number of threads running corePoolSize greater than and less than maximumPoolSize, it would create a new thread to perform the task.

addWorker way

Will first (core number of threads or maximum number of threads) to check whether a new worker thread pool thread according to the current state of the border and the number of threads. If you can create a new one worker thread and boot, then a pass over the task

/**
* 创建新的worker
*
* @param firstTask 提交给线程的任务,要最先执行,可以为 null
* @param core  如果为 true,表示以核心线程数为界创建线程  为 false 表示以最大线程数为界创建线程
* @return
*/
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        // 获取 ctl
        int c = ctl.get();
        // 获取线程池的状态
        int rs = runStateOf(c);

        // 这里的判断条件有点多,拆成 rs>=SHUTDOWN 和 !(rs == SHUTDOWN && firstTask == null &&!workQueue.isEmpty())
        // !(rs == SHUTDOWN && firstTask == null &&!workQueue.isEmpty()) 逆着考虑 ,如下:
        // rs!=SHUTDOWN 也就是为大于 shutdown,为 stop,tidying,terminated
        // firstTask != null
        // workQueue.isEmpty()
        // 如果线程池处于关闭状态,且满足下面条件之一的,不创建 worker
        //      线程池处于 stop,tidying,terminated 状态
        //      firstTask != null
        //      workQueue.isEmpty()
        // 注意:如果线程池处于 shutdown,且 firstTask 为 null,同时队列不为空,允许创建 worker
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

        for (;;) {
            // 获取工作线程数
            int wc = workerCountOf(c);
            // 工作线程数大于最大容量或者工作线程数超过线程数的边界(根据 core 的值取不同的值) 时 不创建worker
            if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
            // 工作线程数 +1  通过 CAS
            // 这里如果失败,表示有并发操作
            if (compareAndIncrementWorkerCount(c))
                // 调出循环,执行真正的创建 worker 逻辑
                break retry;
            // 因为存在并发,需要再读取 ctl 值进行状态判断
            // Re-read ctl
            c = ctl.get();
            // 如果线程状态发生了变化,回到外部循环
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    // 校验已经都通过,开始创建 worker
    // 是否已经启动了 worker
    boolean workerStarted = false;
    // 是否已经添加了 worker
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 把 task 封装成 worker,通过线程工厂创建线程,最后会把任务设置到 Thread 的 target 属性上,后续在执行线程的 start 方法时,就会执行对应的任务的 run 方法
        w = new Worker(firstTask);
        // 获取 worker 中的线程
        final Thread t = w.thread;
        if (t != null) {
            // 获取对象锁
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                // 如果线程池处于 Running 状态 或者 线程池处于 shutdown 状态且任务为 null(执行任务队列中的任务)
                if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                    // precheck that t is startable
                    // 检查线程是否为启动状态,如果为启动状态抛异常
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    // 把新建的 worker 添加到 worker 集中
                    workers.add(w);
                    int s = workers.size();
                    // largestPoolSize 记录 workers 中个数存在过的最大值
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 新建的 worker 添加成功就启动线程,后续有分析
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 线程没有启动成功,对上面创建线程的过程做回滚操作
        if (! workerStarted)
            // 回滚操作,比如把 worker 从 workers 中移除,把线程数减一
            addWorkerFailed(w);
    }
    return workerStarted;
}

复制代码
addWorkerFailed way

Before rollback worker thread creation

private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            // 从 workers 中移除 worker
            workers.remove(w);
        // 把线程数减一
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}

复制代码

Introducing addWorker method, there is a logical thread if woker newly created successfully added to woker set of threads, the thread will start method is called, in fact, the final will be executed to Worker's run method. Because Woker constructor thread by thread factory is created, analyzed as follows

Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    // 这里默认的线程工厂是 DefaultThreadFactory
    this.thread = getThreadFactory().newThread(this);
}

复制代码

Create a thread factory by a thread, the last call Thread's constructor, Thread (... Runnable target ...), tasks to be performed to create a thread as the target parameters, and then call the method after the execution Thread.start run method, and then executes target.run, similar agents

@Override
public void run() {
    if (target != null) {
        // 这个 target 就是创建线程时传递过来的那个任务
        target.run();
    }
}

复制代码

Then you can execute the way we run the worker of the, run method will call runWorker method, let's look at this method

runWorker method

Continue to take the task from the task queue and execute

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 把 status 设置为 0,允许中断
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 从任务队列中获取任务并执行
        while (task != null || (task = getTask()) != null) {
            // 这里加锁是为了避免任务运行期间,其他线程调用 shutdown 方法关闭线程池中正在执行任务的线程
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt

            // 如果线程池状态大于或等于 stop,即 runStateAtLeast(ctl.get(), STOP) 为 true,这个时候就要确保线程是中断的
            // 不用看||后面的条件,直接判断  !wt.isInterrupted(),因为线程池状态为暂停,要确保线程中断,如果没有中断,就要手动中断线程,即执行 wt.interrupt()
            // 如果线程池状态不是 stop,即 runStateAtLeast(ctl.get(), STOP) 为 false,就要确保线程没有中断,这样才能在后面执行任务
            // 这时候需要看 || 后面的 (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)) ,因为要确保线程没有中断,调用Thread.interrupted()清除中断状态,
            // 这里需要再次进行验证线程池的状态,因为可能会有 shutdownNow 的情况
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                // 空方法体,子类可以实现,做一些特殊化处理工作
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 执行任务
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    // 空方法体,子类可以实现,做一些特殊化处理工作
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                // 统计当前 worker 完成了多少任务
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 执行清理工作
        processWorkerExit(w, completedAbruptly);
    }
}

复制代码

In runWorker method, there is a method getTask, the following brief

getTask method
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 满足下面两种情况任意一个就返回 null 同时把线程个数减 1
        // 1.线程池已经处于关闭状态
        // 2.线程池处于 shutdown,且队列为空
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);
        // 判断线程是否有时效性,前面说过,如果把 allowCoreThreadTimeOut 设为 false,那么核心线程数以内的线程是不会关闭的。如果设为 true 就只会存活 keepAliveTime 这么长时间
        // 如果线程数大于核心线程数的线程都有时效性
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 这个判断逻辑就是下面方法总结的第①点和第④点(这段代码我看了足足有半个小时,结合方法的注释终于弄明白了,感觉自己好笨)
        // 超时且超时的 worker 线程需要终止。
        // 如果任务队列非空,要保证当前 worker 线程不是线程池中最后一个线程(如果任务为空,当前线程是线程池中的最后一个线程也无妨,毕竟任务队列为空,当前 worker 线程关闭就关闭了,没影响)
        // 这里的判断条件可以看成 if (wc > maximumPoolSize || ((timed && timedOut) && (wc > 1 || workQueue.isEmpty()))
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 从队列中取任务,分为有超时限制和非超时限制两种情况
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            // 这里会抛出中断异常是因为可能会调用 setMaximumPoolSize 方法,把线程的最大数设置小了,那可能存在当前线程数大于新的最大线程数
            // 这样就得关闭多余的线程,所以重新进入 for 循环,并返回 null
            timedOut = false;
        }
    }
}

复制代码
Summary getTask method

1. Return to task

2. returns null, this case is that the worker thread needs to quit because of the reduced number of threads, the following four possible reasons for this situation

① The number of threads in the pool is greater than the maximum number of threads (as can be provided by a method setMaximumPoolSize)

② thread pool closed [both reject the new task, not the task queue of tasks]

③ thread pool is in shutdown, while the task queue is empty [] reject new tasks

④ overtime and overtime worker thread to terminate. If the task queue is not empty, to ensure that the current thread is not the last thread worker thread pool (if the task is empty, the current thread is the last thread pool thread anyway, after all, the task queue is empty, the current worker thread closes shut down ,No effect)

runWorker there is a method to perform cleanup work processWorkerExit method, the following brief introduction

processWorkerExit method
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 如果是突然完成,需要调整线程数
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 计算线程池完成的任务个数,并从 worke r线程集中删除当前 worker 线程
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    // 尝试把线程池的状态设置为 TERMINATED,该方法在后面分析
    tryTerminate();

    int c = ctl.get();
    // 线程池状态至少为 STOP
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 新建 worker 线程的条件为:当前线程数小于核心线程数或者任务队列不为空但没有运行的线程了(允许核心线程超时的情况下)
        addWorker(null, false);
    }
}

复制代码
tryTerminate way
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 如果处于下面三种任意一种情况,就不能把线程池的状态设为 TERMINATED
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        // 代码执行到这里,说明有资格终止了。但是如果这个时候线程个数非 0,就中断一个空闲的线程来确保 shutdown 信号传播
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 设置线程池状态为 TIDYING
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    // 设置线程池状态为 TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    // 激活调用线程池中因调用 awaitTermination 系列方法而阻塞的线程
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

复制代码

If you look carefully in conjunction with the source of this article here, it should be a little feel for the principle of the implementation of the thread pool, right? The content below the last remaining, the thread pool is closed method

shutdown method
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 权限校验
        checkShutdownAccess();
        // 设置线程池状态为 SHUTDOWN
        advanceRunState(SHUTDOWN);
        // 中断空闲线程
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    // 尝试设置线程池状态为 TERMINATED
    tryTerminate();
}

复制代码
shutdownNow method
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 权限校验
        checkShutdownAccess();
        // 设置线程池状态为 STOP
        advanceRunState(STOP);
        // 中断所有线程
        interruptWorkers();
        // 将任务队列中的任务移动到 tasks 中
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    // 尝试设置线程池状态为 TERMINATED
    tryTerminate();
    return tasks;
}

复制代码

postscript

This article is written holidays as a way to sort out a small piece of thread pool of knowledge, of course, there maybe a problem, white teeth hope you can criticize inheritance and found that the problem may go public No. [sun] message pointed white teeth every day, I can also add letter [micro] dingaiminIT, we discuss the exchange.

Recommended history article

Class loader knowledge vomiting finishing

Ali hung on three sides, fortunately eligible interpolation places, after final surface 5 is eligible offer reputation

Original | ES advertising inverted index architecture evolution and optimization

Advertising architecture and optimized inverted index

cpu usage is too high and too occupied jvm old investigation process

FGC frequently it turned out to be murderers

Years old and 100% occupied, by the way found a bug vertx-redis-client of

KafkaProducer source code analysis

Kafka server source code analysis of the network layer

Redis expiration policy is how to achieve?

Original | HashMap understand if these two points, the interview is no problem

Original | Interviewer: Java objects allocated on the heap must do?

Original | This pavement questions, most people got it wrong

Colleagues: the "Retry" abstract out to be a tool like it

Reference material

  1. juejin.im/entry/59b23…
  2. "Java Programming asynchronous real" welcome public attention [No.] bleached teeth every day for the latest articles, we communicate together, progress together!

Guess you like

Origin juejin.im/post/5e4602d66fb9a07c8a5a0d08
Recommended