netty creates and starts new thread process analysis

Keywords: Netty JDK

This article mainly shares the creation process of NioEventLoop thread bound by server socketchannel

In the server startup process, when the registration operation is executed, it will determine whether the current thread is the thread in NioEventLoop bound by ServerSocketChannel. If not, it will encapsulate the registration operation as a thread task and give it to the thread in NioEventLoop for execution. The relevant code is as follows:

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    if (eventLoop == null) {
        throw new NullPointerException("eventLoop");
    }
    if (isRegistered()) {
        promise.setFailure(new IllegalStateException("registered to an event loop already"));
        return;
    }
    if (!isCompatible(eventLoop)) {
        promise.setFailure(
                new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
        return;
    }

    AbstractChannel.this.eventLoop = eventLoop;

    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {//If the current thread is not the thread in NioEventLoop bound by ServerSocketChannel
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            logger.warn(
                    "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                    AbstractChannel.this, t);
            closeForcibly();
            closeFuture.setClosed();
            safeSetFailure(promise, t);
        }
    }
}

In the code, the eventLoop.inEventLoop() method will be called to determine whether the current thread is a thread in eventLoop. If not, the eventLoop.execute(Runnable runnable) method will be called to submit the task to eventLoop for execution.

The related logic is implemented in the parent class SingleThreadEventxecutor class:

/**
* When executing the registration logic, the current thread is the main thread
*/
@Override
public void execute(Runnable task) {
    if (task == null) {//Validity verification
        throw new NullPointerException("task");
    }
	//Whether the current thread is a thread in EventLoop bound by ServerSocketChannel
    //Current thread is main thread, so inEventLoop should be false
    boolean inEventLoop = inEventLoop(); 
    addTask(task);//Add thread task to task queue in EventLoop
    if (!inEventLoop) {
        startThread();//Startup thread
        //If the thread has been closed, remove the task submitted to the task queue, and call the rejection policy to reject the execution of the task
        if (isShutdown() && removeTask(task)) {
            reject();
        }
    }

    if (!addTaskWakesUp && wakesUpForTask(task)) {
        wakeup(inEventLoop);
    }
}

Add a thread task to the thread's task queue

protected void addTask(Runnable task) {
    if (task == null) {
        throw new NullPointerException("task");
    }
    if (!offerTask(task)) {
        reject(task);
    }
}

Create and start threads

Status enumeration of threads

key value Meaning
ST_NOT_STARTED 1 Thread not started
ST_STARTED 2 Thread started
ST_SHUTTING_DOWN 3 Thread shutting down
ST_SHUTDOWN 4 Thread closed
ST_TERMINATED 5 Thread terminated
//If the thread has not been started, Unsafe is called to modify the corresponding properties of the thread
//Call the doStartThread() method to start the thread
private void startThread() {
    if (state == ST_NOT_STARTED) {
        if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
            try {
                doStartThread();
            } catch (Throwable cause) {
                STATE_UPDATER.set(this, ST_NOT_STARTED);
                PlatformDependent.throwException(cause);
            }
        }
    }
}
private void doStartThread() {
    assert thread == null;//Assert that the current thread is empty
    //Call the execute(Runnable task) method of threadpertask executor to execute the create thread task
    executor.execute(new Runnable() {
        @Override
        public void run() {
            thread = Thread.currentThread();
            if (interrupted) {
                thread.interrupt();
            }

            boolean success = false;
            updateLastExecutionTime();//Record execution time
            try {
                SingleThreadEventExecutor.this.run();
                success = true;
            } catch (Throwable t) {
                logger.warn("Unexpected exception from an event executor: ", t);
            } finally {
                for (;;) {
                    int oldState = state;
                    if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
                            SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
                        break;
                    }
                }

                // Check if confirmShutdown() was called at the end of the loop.
                if (success && gracefulShutdownStartTime == 0) {
                    if (logger.isErrorEnabled()) {
                        logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
                                SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " +
                                "be called before run() implementation terminates.");
                    }
                }

                try {
                    // Run all remaining tasks and shutdown hooks.
                    for (;;) {
                        if (confirmShutdown()) {
                            break;
                        }
                    }
                } finally {
                    try {
                        cleanup();
                    } finally {
                        STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
                        threadLock.release();
                        if (!taskQueue.isEmpty()) {
                            if (logger.isWarnEnabled()) {
                                logger.warn("An event executor terminated with " +
                                        "non-empty task queue (" + taskQueue.size() + ')');
                            }
                        }

                        terminationFuture.setSuccess(null);
                    }
                }
            }
        }
    });
}

ThreadPerTaskExecutor

The threadpertaskexecutior class is as follows

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        if (threadFactory == null) {
            throw new NullPointerException("threadFactory");
        }
        this.threadFactory = threadFactory;
    }

    @Override
    public void execute(Runnable command) {
        threadFactory.newThread(command).start();//Call the NewThread method of thread engineering to create a thread and start it
    }
}

The code of the newThread method of the DefaultThreadFactory Thread factory is as follows, that is, the Thread created through the DefaultThreadFactory Thread factory is the FastThreadLocalThread class, which is an extension of Netty to the JDK native Thread class. The following article analyzes the class in detail, which can be temporarily considered as Thread.

@Override
public Thread newThread(Runnable r) {
    Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
    try {
        if (t.isDaemon() != daemon) {//Set whether the thread is a guardian thread
            t.setDaemon(daemon);
        }

        if (t.getPriority() != priority) {//Set thread priority
            t.setPriority(priority);
        }
    } catch (Exception ignored) {
    }
    return t;
}

protected Thread newThread(Runnable r, String name) {
    return new FastThreadLocalThread(threadGroup, r, name);
}

The relationship among NioEventLoop, SingleThreadEventExecutor and threadpertask executor

The relationships among NioEventLoop, SingleThreadEventExecutor and threadpertask executor are as follows:

The main function of SingleThreadEventExecutor class is to execute thread tasks

The main function of threadpertakexecutior class is to create Netty threads

SingleThreadEventExecutor class is the parent class of NioEventLoop, and threadpertakeexecutor is a member property of SingleThreadEventExecutor, which is initialized by the constructor.

[failed to save the image in the external link. The source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-haqfcnkv-157951393050) (/ users / lijiaxing / desktop / book / article image to be read / NioEventLoop, SingleThreadEventExecutor and threadpertask executor relationship. png))

eventLoop.execute(Runnable task) —>

SingleThreadEventExecutor.execute(Runnable task)—>

startThread()—>

doStartThread();—>

executor.execute(Runnable task)—>

ThreadPerTaskExecutor.execute(Runnable command)—>

DefaultThreadFactory.newThread(Runnable r)

summary

1. Create a thread when EventLoop performs a thread task, so the thread in the EventLoop of Netty is Lazy create

2. The call chain to create the EventLoop thread is

eventLoop.execute(Runnable task)

​ —>SingleThreadEventExecutor.execute(Runnable task)

​ —>startThread()

​ —>doStartThread()

​ —>executor.execute(Runnable task)

​ —>ThreadPerTaskExecutor.execute(Runnable command)

​ —>DefaultThreadFactory.newThread(Runnable r)

48 original articles published, 41 praised, 90000 visitors+
Private letter follow

Posted by scotthoff on Sun, 19 Jan 2020 22:49:35 -0800