Java synchronized关键字详解:实例锁、类锁与防重放机制实战

竹里
竹里
发布时间:2026-10-13| 4人浏览过
原创

本文详细讲解Java中synchronized关键字在实例锁与类锁上的区别,分析其底层加锁逻辑,并结合实际业务场景中的Token生成与验证代码,演示如何在高并发环境下通过同步机制保证数据一致性与操作安全性。

synchronized的作用与核心概念

synchronized是Java语言的关键字,主要用于实现线程间的同步互斥。它通过确保同一时刻只有一个线程执行被保护的代码块或方法,来解决资源竞争问题,防止因多线程并发访问导致的数据不一致。理解其锁对象的归属是正确使用该关键字的前提。

两种主要的加锁方式对比

第1步:使用非静态同步(实例锁)

目的:对某个特定对象实例进行保护,确保该实例的方法在同一时间内不被多个线程交叉执行。
入口:直接修饰非静态方法,或在方法内部同步 this 引用。
动作:当 synchronized 修饰非静态方法或作为实例对象的锁时,锁的粒度是当前的实例对象(即 this)。
参数与表现:如果存在两个不同对象 a 和 b(同为某类的实例),线程A调用 a 的同步方法,线程B调用 b 的同步方法,两者不会形成互斥,因为锁的对象不同。只有同一实例在不同线程中被调用时,才会发生阻塞等待。

// 方式一:直接修饰方法
public synchronized void method(){ 
  // ……临界区代码
} 

// 方式二:手动同步this对象
public void method() 
{ 
  synchronized (this){ 
    // ……临界区代码
  } 
}

第2步:使用静态同步(类锁)

目的:对整个类级别进行保护,无论创建多少个实例,该类的所有静态同步操作都互斥。
入口:修饰静态方法,或在任何地方(含非静态方法)同步 类名.class。
动作:当 synchronized 修饰静态方法或同步 Main.class 时,锁的对象是该类的Class对象(元数据层)。
参数与表现:即使是不同的对象实例,只要涉及类的静态资源或类级别的同步代码块,就会形成互斥。这是一种比实例锁更宽泛的控制方式。

class Main { 
  // 静态方法,锁定的是Main.class对象
  public synchronized static void method1(){ 
    // …. 
  } 
  
  public void method2(){ 
    synchronized(Main.class) { // 锁定的是Main.class对象
       // ……
    }
  } 
}

实战案例:基于synchronized的Controller Token管理

在实际的后端开发中,为了防范CSRF攻击或重复提交,常采用一次性Token机制。下面以一段Spring MVC风格的抽象控制器为例,解析如何利用 synchronized 安全地管理Session中的Token状态。

第3步:Token的唯一性生成逻辑

背景:系统需要在用户会话中保存Token,并确保生成的GUID全局唯一且操作原子性。
风险点:springmvc_token 是一个静态Map。如果在高并发下多个线程同时进入生成流程,可能导致数据读取不一致或覆盖错误。
操作顺序:

  • 获取当前请求对应的Session对象。
  • 进入 synchronized (springmvc_token) 同步块。
  • 检查Session中是否已有旧Token,如果有则加载到本地变量(注意:原代码此处写法虽不严谨但意图明确,即维护一个引用)。
  • 生成唯一的GUID字符串。
  • 将新Token存入共享的 springmvc_token Map,并更新Session属性。
package com.asiainfolinkage.ems.web.controller.base;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public abstract class TokenBaseController extends BaseController {
  // 静态变量,所有实例共享
  private static Map<String, String> springmvc_token = new HashMap<String, String&gt(); 

  /** 生成一个唯一值的token */
  public String generateGUID(HttpSession session) {
    String token = "";
    // Date date = new Date(); // 原文中有此行但未使用,可优化掉
    synchronized (springmvc_token) {
      try {
        Object obj = session.getAttribute(Constants.SPRING_MVC_TOKENNAME);
        if (obj != null)
          springmvc_token = (Map<String, String>) session.getAttribute(Constants.SPRING_MVC_TOKENNAME);
        
        // 关键步骤:在无其他线程干扰的情况下生成并写入
        token = new BigInteger(165, new Random()).toString(36).toUpperCase();
        springmvc_token.put(Constants.DEFAULT_TOKEN_NAME + "." + token, token);
        session.setAttribute(Constants.SPRING_MVC_TOKENNAME, springmvc_token);
        Constants.TOKEN_VALUE = token;
      } catch (IllegalStateException e) {
        _log.error("generateGUID() mothod find bug,by token session...");
      }
    }
    return token;
  }
  // ... 其余代码省略
}

第4步:Token的安全性验证逻辑

目的:验证前端提交的表单数据是否包含合法且在有效期内的一次性Token。
操作流程:

  1. 从请求参数中提取输入Token。
  2. 若参数为空,直接返回无效。
  3. 从Session中取出Token集合。若不存在,视为无效。
  4. 根据前缀拼接全量Key,查找匹配的SessionToken。
  5. 比对输入Token与服务端存储的SessionToken。
  6. 消耗机制:一旦验证成功,立即从 tokenMap 中移除该Token,从而防止重放攻击(Replay Attack)。
  /** 验证表单token值和session中的token值是否一致 */
  public boolean validToken(HttpServletRequest request) {
    String inputToken = getInputToken(request);

    if (inputToken == null) {
      _log.warn("token is not valid!inputToken is NULL");
      return false;
    }
    HttpSession session = request.getSession();
    Map<String, String> tokenMap = (Map<String, String>) session.getAttribute(Constants.SPRING_MVC_TOKENNAME);
    
    if (tokenMap == null || tokenMap.size() < 1) {
      _log.warn("token is not valid!sessionToken is NULL");
      return false;
    }
    
    // 匹配逻辑
    String sessionToken = tokenMap.get(Constants.DEFAULT_TOKEN_NAME + "." + inputToken);
    if (!inputToken.equals(sessionToken)) {
      _log.warn("token is not valid!inputToken='" + inputToken + "',sessionToken = '" + sessionToken + "'");
      return false;
    }
    
    // 验证成功后,删除Token以防止重用
    tokenMap.remove(Constants.DEFAULT_TOKEN_NAME + "." + inputToken);
    session.setAttribute(Constants.SPRING_MVC_TOKENNAME, tokenMap);

    return true;
  }

  /** 获取表单中token值 */
  private String getInputToken(HttpServletRequest request) {
    Map params = request.getParameterMap();
    if (!params.containsKey(Constants.DEFAULT_TOKEN_NAME)) {
      _log.warn("Could not find token name in params.");
      return null;
    }
    String[] tokens = (String[]) (String[]) params.get(Constants.DEFAULT_TOKEN_NAME);
    if ((tokens == null) || (tokens.length < 1)) {
      _log.warn("Got a null or empty token name.");
      return null;
    }
    return tokens[0];
  }
}

以上就是关于Java中synchronized关键字及其在安全控制中应用的详细内容,更多关于Java并发编程与线程安全的资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。