When threadlocal hit the thread pool,

Source: Internet
Author: User
Tags wrapper

Threadlocal use

Threadlocal can let threads have local variables, in a Web environment, in order to facilitate code decoupling, we usually use it to save context information, and then use a Util class to provide access to the portal, from the controller layer to the service layer can easily get the context. Let's take a look at the threadlocal by code.

Create a new Threadcontext class to hold thread context information

public class ThreadContext {    private static ThreadLocal<UserObj> userResource = new ThreadLocal<UserObj>();    public static UserObj getUser() {        return userResource.get();    }    public static void bindUser(UserObj user) {        userResource.set(user);    }    public static UserObj unbindUser() {        UserObj obj = userResource.get();        userResource.remove();        return obj;    }}

Create a new sessionfilter that is used to manipulate thread variables

    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) servletRequest;        try {            // 假设这里是从cookie拿token信息, 调用服务/或者从缓存查询用户信息            // 为了避免后续逻辑中多次查询/请求缓存服务器, 这里拿到user后放到线程本地变量中            UserObj user = ThreadContext.getUser();            // 如果当前线程中没有绑定user对象,那么绑定一个新的user            if (user == null) {                ThreadContext.bindUser(new UserObj("usertest"));            }            filterChain.doFilter(servletRequest, servletResponse);        } finally {            // ThreadLocal的生命周期不等于一次request请求的生命周期            // 每个request请求的响应是tomcat从线程池中分配的线程, 线程会被下个请求复用.            // 所以请求结束后必须删除线程本地变量            // ThreadContext.unbindUser();        }    }

New Userutils Tool Class

/** * 配合SessionFilter使用,从上下文中取user信息 */public class UserUtils {    public static UserObj getCurrentUser() {        return ThreadContext.getUser();    }}

Create a new servlet test

public class HelloworldServlet extends HttpServlet {    private static Logger logger = LoggerFactory.getLogger(HelloworldServlet.class);    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        UserObj user = UserUtils.getCurrentUser();        logger.info(user.getName() + user.hashCode());        super.doGet(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        super.doGet(req, resp);    }}

Loop request Servlet, the console displays the results as follows. You can see that the initial size of the Tomcat thread pool is 10, and the subsequent requests reuse the previous thread, and the hashcode of the user object in Threadcontext is the same.

2016-11-29 17:21:35.975 INFO 36672---[nio-8080-exec-2] com.zallds.xy.servlet.HelloworldServlet: usertest8182026732016-11-29 17:21:38.923 INFO 36672---[nio-8080-exec-3] com.zallds.xy.servlet.HelloworldServlet: usertest15825917022016-11-29 17:21:45.810 INFO 36672---[nio-8080-exec-4] com.zallds.xy.servlet.HelloworldServlet: usertest557550372016-11-29 17:21:46.773 INFO 36672---[nio-8080-exec-5] com.zallds.xy.servlet.HelloworldServlet: usertest14954668072016-11-29 17:21:47.345 INFO 36672---[nio-8080-exec-6] com.zallds.xy.servlet.HelloworldServlet: usertest11493602452016-11-29 17:21:47.613 INFO 36672---[nio-8080-exec-7] com.zallds.xy.servlet.HelloworldServlet: usertest5183753392016-11-29 17:21:47.837 INFO 36672---[nio-8080-exec-8] com.zallds.xy.servlet.HelloworldServlet: usertest924589922016-11-29 17:21:48.012 INFO 36672---[nio-8080-exec-9] com.zallds.xy.servlet.HelloworldServlet: usertest9448670342016-11-29 17:21:48.199 INFO 36672---[io-8080-exec-10] com.zallds.xy.servlet.helloworldservlet:usertest14109728092016-11-29 17:21:48.378 INFO 36672---[nio-8080-exec-1 ] com.zallds.xy.servlet.helloworldservlet:usertest8053320462016-11-29 17:21:48.552 INFO 36672---[nio-8080-exec-2] C om.zallds.xy.servlet.helloworldservlet:usertest8182026732016-11-29 17:21:48.730 INFO 36672---[nio-8080-exec-3] com.zallds.xy.servlet.helloworldservlet:usertest15825917022016-11-29 17:21:48.903 INFO 36672---[nio-8080-exec-4] com.zallds.xy.servlet.helloworldservlet:usertest557550372016-11-29 17:21:49.072 INFO 36672---[nio-8080-exec-5] com.zallds.xy.servlet.helloworldservlet:usertest14954668072016-11-29 17:21:49.247 INFO 36672---[nio-8080-exec-6] com.zallds.xy.servlet.helloworldservlet:usertest11493602452016-11-29 17:21:49.402 INFO 36672---[nio-8080-exec-7] com.zallds.xy.servlet.HelloworldServlet:usertest518375339

Remove Comments//Threadcontext.unbinduser (); Re-request, each time the user object from Threadlocal is completely different.

2016-11-29 17:30:37.150 INFO 36903---[nio-8080-exec-1] com.zallds.xy.servlet.HelloworldServlet: usertest4131385712016-11-29 17:30:42.932 INFO 36903---[nio-8080-exec-2] com.zallds.xy.servlet.HelloworldServlet: usertest14021919452016-11-29 17:30:43.124 INFO 36903---[nio-8080-exec-3] com.zallds.xy.servlet.HelloworldServlet: usertest19575791732016-11-29 17:30:43.313 INFO 36903---[nio-8080-exec-4] com.zallds.xy.servlet.HelloworldServlet: usertest15825917022016-11-29 17:30:43.501 INFO 36903---[nio-8080-exec-5] com.zallds.xy.servlet.HelloworldServlet: usertest19174795822016-11-29 17:30:43.679 INFO 36903---[nio-8080-exec-6] com.zallds.xy.servlet.HelloworldServlet: usertest7720367672016-11-29 17:30:43.851 INFO 36903---[nio-8080-exec-7] com.zallds.xy.servlet.HelloworldServlet: usertest1620207612016-11-29 17:30:44.024 INFO 36903---[nio-8080-exec-8] com.zallds.xy.servlet.HelloworldServlet: usertest6822329502016-11-29 17:30:44.225 INFO 36903---[nio-8080-exEC-9] com.zallds.xy.servlet.helloworldservlet:usertest21406503412016-11-29 17:30:44.419 INFO 36903---[ IO-8080-EXEC-10] com.zallds.xy.servlet.helloworldservlet:usertest13276017632016-11-29 17:30:44.593 INFO 36903---[ Nio-8080-exec-1] com.zallds.xy.servlet.helloworldservlet:usertest6477384112016-11-29 17:30:44.787 INFO 36903---[ Nio-8080-exec-2] com.zallds.xy.servlet.helloworldservlet:usertest9448670342016-11-29 17:30:45.045 INFO 36903---[ Nio-8080-exec-3] com.zallds.xy.servlet.helloworldservlet:usertest18861545202016-11-29 17:30:45.317 INFO 36903---[ NIO-8080-EXEC-4] com.zallds.xy.servlet.helloworldservlet:usertest15929042732016-11-29 17:30:46.380 INFO 36903---[ NIO-8080-EXEC-5] com.zallds.xy.servlet.helloworldservlet:usertest14109728092016-11-29 17:30:46.524 INFO 36903---[ NIO-8080-EXEC-6] com.zallds.xy.servlet.helloworldservlet:usertest17055706892016-11-29 17:30:46.692 INFO 36903---[ NIO-8080-EXEC-7] Com.zallds.xy.servlet.helloworldservlet:usertest11051343752016-11-29 17:30:46.802 INFO 36903---[nio-8080-exec-8] com.zallds.xy.servlet.HelloworldServlet: usertest407377722
Threadlocal Sub-threading scenario

The need to add an action to send a message to the user in the original business logic. Send mail we use asynchronous processing to create a new thread to execute.

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        UserObj user = UserUtils.getCurrentUser();        logger.info(user.getName() + user.hashCode());        SendEmailTask emailThread = new SendEmailTask();        new Thread(emailThread).start();        super.doGet(req, resp);    }    class SendEmailTask implements Runnable {        @Override        public void run() {            UserObj user = UserUtils.getCurrentUser();            logger.info("子线程中:" + (user == null ? "user为null" : user.getName() + user.hashCode()));        }    }

Create an asynchronous thread in the main thread, can you get it in the child thread? It's not possible to pass the test.

2016-11-29 18:09:16.482  INFO 38092 --- [nio-8080-exec-2] com.zallds.xy.servlet.HelloworldServlet  : usertest14255059182016-11-29 18:09:16.483  INFO 38092 --- [       Thread-4] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:user为null2016-11-29 18:09:20.995  INFO 38092 --- [nio-8080-exec-3] com.zallds.xy.servlet.HelloworldServlet  : usertest12803735522016-11-29 18:09:20.996  INFO 38092 --- [       Thread-5] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:user为null

How does a child thread get the threadlocal data for the parent thread? The JDK gives us a workaround, Threadlocal has a subclass inheritablethreadlocal, When creating threadlocal we use the Inheritablethreadlocal class to implement a child thread to obtain a local variable to the parent thread.

private static ThreadLocal<UserObj> userResource = new InheritableThreadLocal<UserObj>();

The user object is then normally available in the child thread.

2016-11-29 19:07:01.518  INFO 39644 --- [nio-8080-exec-2] com.zallds.xy.servlet.HelloworldServlet  : usertest4955501282016-11-29 19:07:01.518  INFO 39644 --- [       Thread-4] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest4955501282016-11-29 19:07:05.839  INFO 39644 --- [nio-8080-exec-3] com.zallds.xy.servlet.HelloworldServlet  : usertest18517174042016-11-29 19:07:05.840  INFO 39644 --- [       Thread-5] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest1851717404
ThreadLocal child thread passing-thread pool scenario

When we perform asynchronous tasks, most of the thread pool mechanisms are used (such as executor). There is a problem, even if the parent thread has ended and the child thread is still present and pooled. In this way, the threads in the thread pool will not return the variables set in the current thread when the next request is executed, because "child threads" in the pool are not created at all by the current thread, threadlocal. The threadlocal variable that is set by the current thread cannot be passed to threads in the thread pool.

We modify the code that sends the message and use the thread pool instead.

2016-11-29 19:51:51.973  INFO 40937 --- [nio-8080-exec-1] com.zallds.xy.servlet.HelloworldServlet  : usertest14176412612016-11-29 19:51:51.974  INFO 40937 --- [pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest14176412612016-11-29 19:51:55.746  INFO 40937 --- [nio-8080-exec-2] com.zallds.xy.servlet.HelloworldServlet  : usertest11165379552016-11-29 19:51:55.746  INFO 40937 --- [pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest14176412612016-11-29 19:51:58.825  INFO 40937 --- [nio-8080-exec-3] com.zallds.xy.servlet.HelloworldServlet  : usertest14899388562016-11-29 19:51:58.826  INFO 40937 --- [pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest1417641261

You can find that the task of sending the mail three times is the same thread [Pool-1-thread-1], the first child thread is the same as the user object in the parent thread, and the user object in the following "Child Threads" (previously mentioned, which is not already a child thread) is the same as in the first parent thread.

So, in the context of the online pool, how can the "sub-thread" Get the variable that the parent thread path handed over? It would be nice if we were able to deliver the past when we created the task. Follow this idea and we'll implement it.

Continue to modify the code

    protected void Doget (HttpServletRequest req, HttpServletResponse resp) throws Servletexception, IOException {U        Serobj user = Userutils.getcurrentuser ();        Logger.info (User.getname () + User.hashcode ());        Sendemailtask emailthread = new Sendemailtask ();        Executor.execute (New userrunnable (Emailthread, user));    Super.doget (req, resp);         }/** * Make a wrapper, wrap the target task in a layer, pass the thread local variable in the Run method */class Userrunnable implements Runnable {/**        * Target Task Object */Runnable Runnable;        /** * The user object to bind */userobj user;            Public userrunnable (Runnable Runnable, userobj user) {this.runnable = Runnable;        This.user = user;            } @Override public void Run () {threadcontext.binduser (user);            Runnable.run ();        Threadcontext.unbinduser ();     }} class Sendemailtask implements Runnable {@Override public void run () {       Userobj user = Userutils.getcurrentuser (); Logger.info ("In Child Threads:" + (user = = null? ")        User is null ": User.getname () + User.hashcode ())); }    }

Request again, get the results we want.

2016-11-29 20:04:12.153  INFO 41258 --- [nio-8080-exec-1] com.zallds.xy.servlet.HelloworldServlet  : usertest15651807442016-11-29 20:04:12.154  INFO 41258 --- [pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest15651807442016-11-29 20:04:14.142  INFO 41258 --- [nio-8080-exec-2] com.zallds.xy.servlet.HelloworldServlet  : usertest4813967042016-11-29 20:04:14.142  INFO 41258 --- [pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest4813967042016-11-29 20:04:15.248  INFO 41258 --- [nio-8080-exec-3] com.zallds.xy.servlet.HelloworldServlet  : usertest4007173952016-11-29 20:04:15.249  INFO 41258 --- [pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet  : 子线程中:usertest400717395

To this end, threadlocal common scenarios and corresponding solutions should be met. The next step is how to use it in practical applications.

In order to elicit the original intention of the article and what is to be said later, we can further improve the final solution.

    ThreadContext.bindUser(user);    runnable.run();    ThreadContext.unbindUser();

This place is directly overwritten when bind, unable to save and restore the state before the thread. To do this, we can abstract a threadstate to save the state of the thread, save the original before bind, and restore it after the task is executed.

public interface ThreadState {void bind ();    void Restore (); void Clear ();    public class Userthreadstate implements ThreadState {private Userobj original;    Private Userobj user;    Public userthreadstate (userobj user) {this.user = user;        } @Override public void bind () {this.original = Threadcontext.getuser ();    Threadcontext.binduser (This.user);    } @Override public void Restore () {threadcontext.binduser (this.original);    } @Override public void Clear () {threadcontext.unbinduser (); }}protected void Doget (HttpServletRequest req, HttpServletResponse resp) throws Servletexception, IOException {use        RobJ user = Userutils.getcurrentuser ();        Logger.info (User.getname () + User.hashcode ());        Sendemailtask emailthread = new Sendemailtask ();        Executor.execute (New Userrunnable (Emailthread, new userthreadstate (user));    Super.doget (req, resp); }/** * Make a wrapper, wrap the target task in a layer, pass the line in the Run methodProcess local variable */class Userrunnable implements Runnable {/** * Target Task Object */Runnable Runnable;        /** * The user object to bind */Userthreadstate userthreadstate;            Public userrunnable (Runnable Runnable, userthreadstate userthreadstate) {this.runnable = Runnable;        This.userthreadstate = userthreadstate;            } @Override public void Run () {userthreadstate.bind ();            Runnable.run ();            Userthreadstate.restore ();            Userobj Userorig = Userutils.getcurrentuser ();        Logger.info ("Original:" + userorig.getname () + Userorig.hashcode ()); }} class Sendemailtask implements Runnable {@Override public void run () {Userobj user =            Userutils.getcurrentuser (); Logger.info ("In Child Threads:" + (user = = null? ")        User is null ": User.getname () + User.hashcode ())); }    }

The

Implementation effect is the same, as for why the original object is the same three times, and should be understood by the preceding instructions

2016-11-29 20:19:48.694 INFO 41671---[nio-8080-exec-1] com.zallds.xy.servlet.HelloworldServlet: usertest1147606762016-11-29 20:19:48.699 INFO 41671---[pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet: Sub-thread: usertest1147606762016-11-29 20:19:48.700 INFO 41671---[pool-1-thread-1] com.zallds.xy.servlet.helloworldservlet:original:usertest1147606762016-11-29 20:19:57.123 INFO 41671---[ Nio-8080-exec-2] com.zallds.xy.servlet.helloworldservlet:usertest9413021992016-11-29 20:19:57.123 INFO 41671---[ POOL-1-THREAD-1] Com.zallds.xy.servlet.HelloworldServlet: Sub-thread: usertest9413021992016-11-29 20:19:57.123 INFO 41671-  --[pool-1-thread-1] com.zallds.xy.servlet.helloworldservlet:original:usertest1147606762016-11-29 20:20:04.385  INFO 41671---[nio-8080-exec-3] com.zallds.xy.servlet.helloworldservlet:usertest14899388562016-11-29 20:20:04.385 INFO 41671---[pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet: In child threads: usertest14899388562016-11-29 20:20:04.341671 INFO---[pool-1-thread-1] com.zallds.xy.servlet.HelloworldServlet:original:usertest114760676 

As a result of the problem in the Securityutils.getsubject () process using the Shiro framework, the example of this article is given, and some of the code in the example references the implementation mechanism of the Shiro framework. Later, we will look at the subject related design of Shiro.

Http://shiro.apache.org/subject.html



99793933e682
Links: http://www.jianshu.com/p/85d96fe9358b
Source: Pinterest
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.

When threadlocal hit the thread pool,

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.