ThreadLocal 和Synchronized


    並發都用到, 兩個都是解決了線程並發問題,區別呢?

Synchronized  同步機制, 共有變量被多個線程使用,會出現線程不安全,所以,加鎖,鎖機制很多種,例如Synchronized, lock 鎖, 阻塞隊列。以時間換空間, 某一個線程拿到鎖, 先進去, 其他線程, 等着吧~

ThreadLocal ,當多個線程使用一個對象, 以空間換時間, 創建不同的變量副本。大家不要搶, 每個人都有!首先這個對象是全局變量。

ThreadLocal 保證了線程的隔離性 ,一個經典的例子是 SimpleDateFormat 線程不安全問題, 這個時間格式化 工具類我們使用頻率非常大 。

Synchronized使用場景:比如1000 張火車票, 每個線程操作, 必須保證每個窗口賣 不同 的火車票, 保證座位不相同! 這1000 張票是原子性的。

ThreadLocal使用場景:舉例: spring security管理權限  , 每個用戶用戶名,密碼驗證登錄會存在 Authentication中, 包括用戶名密碼, 是否被鎖定, 是否過期,等很多消息, 這是一個對象, 當用戶登錄之后, 使用這個系統, 比如查看自己的訂單,自己的購物車,自己的余額 ,這里Authentication 就是全局變量,如果多個用戶登錄,這個對象肯定會被改變, 簡單的方法是 每一個用戶 授權登錄后, 都生成一個全局變量對象, 但是1000 萬 個用戶呢,系統會不會垮掉?肯定是!

 這就是線程並發非同步的另一種領域,數據隔離,保證每個用戶有各自的數據, 用同步加鎖是不能解決的。

 所以可以創建一個threadLocal , 看看  spring security 的源碼:

 

/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 *
 * Licensed 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 org.springframework.security.core.context;

import org.springframework.util.Assert;

/**
 * A <code>ThreadLocal</code>-based implementation of
 * {@link SecurityContextHolderStrategy}.
 *
 * @author Ben Alex
 *
 * @see java.lang.ThreadLocal
 * @see org.springframework.security.core.context.web.SecurityContextPersistenceFilter
 */
final class ThreadLocalSecurityContextHolderStrategy implements
        SecurityContextHolderStrategy {
    // ~ Static fields/initializers
    // =====================================================================================
       //核心在這里 , 這里創建 一個 threadLocal 的 SecurityContext
    private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();

    // ~ Methods
    // ========================================================================================================

    public void clearContext() {
        contextHolder.remove();
    }

    public SecurityContext getContext() {
        SecurityContext ctx = contextHolder.get();

        if (ctx == null) {
            ctx = createEmptyContext();
            contextHolder.set(ctx);
        }

        return ctx;
    }

    public void setContext(SecurityContext context) {
        Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
        contextHolder.set(context);
    }

    public SecurityContext createEmptyContext() {
        return new SecurityContextImpl();
    }
}

 

 

再看看線程全局變量怎么被設置進去

 

 

       //這個是登錄方法, 用戶名密碼登錄
public void login(String username, String password) throws ServletException { if (isAuthenticated()) { throw new ServletException("Cannot perform login for '" + username + "' already authenticated as '" + getRemoteUser() + "'"); } AuthenticationManager authManager = authenticationManager; if (authManager == null) { logger.debug("authenticationManager is null, so allowing original HttpServletRequest to handle login"); super.login(username, password); return; } Authentication authentication; try {
//這里去數據庫驗證, 用戶名錯誤?用戶名過期?用戶被鎖定?用戶什么角色 authentication
= authManager .authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (AuthenticationException loginFailed) { SecurityContextHolder.clearContext(); throw new ServletException(loginFailed.getMessage(), loginFailed); }
//這里全是全局變量 設置進去 SecurityContextHolder.getContext().setAuthentication(authentication); }

 

好了, 一個驗證通過了, 並被設置到threadLocal ,

現在用戶刪除訂單

    @RequestMapping(value = "/deleteId", method = RequestMethod.POST)
    @ResponseBody
    public JSONObject deleteId(@RequestParam("strJson")String json) {
//SecurityContextHolder 是一個全局變量, 直接在方法里面獲取 Authentication auth
= SecurityContextHolder.getContext() .getAuthentication(); Integer role=-1; Object pinciba = auth.getPrincipal(); if (pinciba instanceof Customer) { role =((Customer) pinciba).getRole(); } if(role==2){ //管理員刪除 }else{ //非管理員不能刪除 } JSONObject jsons = new JSONObject(); System.out.println(json); return jsons; }

threadlocal 很強大, 一種弱引用。我們知道Java有四中對象:

   從JDK1.2版本開始,把對象的引用分為四種級別,從而使程序能更加靈活的控制對象的生命周期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。

 threadLocal 就是一種弱引用, 當某個用戶很長時間沒有登錄, 而 其他用戶需要threadLocal 添加一個自己的 變量, 就會回收沒有使用了, 實現了線程數據隔離吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM