【java】【服务器】线程上下文丢失 是指什么
目录
■前言
■正文开始
线程上下文的核心组成部分
为什么会出现上下文丢失?
直观示例说明
为什么上下文如此重要?
解决上下文丢失的关键
总结
■如果我想在servlet中使用线程,代码应该如何实现
推荐方案:使用 ManagedExecutorService(WebSphere 托管线程池)
备选方案:手动管理线程上下文(如果无法使用 ManagedExecutorService)
关键配置步骤(WebSphere 控制台)
两种方案对比
最佳实践建议
完整示例(生产级代码)
■前言
Web应用中,为了提高效率,某段和主业务无关的处理,使用异步处理来处理。
(使用的服务器是WebSphere)
结果报如下错误
webcontexts service getStandard Context Failed to retrieve application name
这个错误的原因是线程上下文丢失造成的,
因此,整理解释一下什么是线程上下文丢失
========================================
■正文开始
线程上下文的核心组成部分
-
类加载器(ClassLoader)
-
Web 应用有独立的类加载器(隔离其他应用)
-
负责加载应用中的类、资源和库
-
丢失后果:
ClassNotFoundException
、NoClassDefFoundError
-
-
JNDI(Java Naming and Directory Interface)上下文
-
提供对应用服务器资源的访问(如数据源、JMS 队列)
-
丢失后果:
NamingException
、无法查找java:comp/env
资源
-
-
Web 应用上下文(ServletContext)
-
包含 Web 应用元数据:应用名称、上下文路径、初始化参数
-
丢失后果:
getStandardContext failed to retrieve application name
(我遇到的错误)
-
-
安全上下文(Security Context)
-
包含用户认证/授权信息(如 Principal、角色)
-
丢失后果:
NullPointerException
或权限检查失败
-
-
事务上下文(Transaction Context)
-
管理数据库事务边界
-
丢失后果:事务无法提交/回滚
-
为什么会出现上下文丢失?
-
线程创建方式
// 自定义线程不会继承上下文 new Thread(() -> {// 此处丢失所有上下文! }).start();
-
Web 容器管理的线程 vs 自定义线程
特性 Web 容器线程 (如 HTTP 请求线程) 自定义线程 类加载器 自动设置正确 默认使用系统类加载器 JNDI 上下文 自动可用 InitialContext()
失败ServletContext 通过 getServletContext()
获取返回 null
或抛出异常事务传播 支持 事务边界中断 -
WebSphere 的上下文隔离机制
-
为每个应用创建独立的沙箱环境
-
自定义线程被视为"外部线程",无权访问应用沙箱
-
直观示例说明
假设在 Servlet 中启动线程:
public class MyServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 正确环境 (有上下文)String appName = getServletContext().getContextPath(); // 成功获取new Thread(() -> {// 危险区域 (上下文丢失)!try {// 尝试获取相同信息Context ctx = new InitialContext();String name = (String) ctx.lookup("java:app/AppName"); // 抛出异常!} catch (NamingException e) {// 报错: getStandardContext failed to retrieve application name}}).start();}
}
为什么上下文如此重要?
当调用以下 API 时会依赖上下文:
// 依赖 Web 上下文
servletContext.getResource("/WEB-INF/config.xml"); // 依赖 JNDI 上下文
dataSource = (DataSource) new InitialContext().lookup("jdbc/myDB");// 依赖类加载器
Class.forName("com.myapp.CustomClass"); // 依赖安全上下文
request.isUserInRole("Admin");
解决上下文丢失的关键
必须通过以下方式之一传递上下文:
-
显式传递数据
// 在 Web 线程中预先获取值 String appName = getServletContext().getContextPath(); executor.submit(new MyTask(appName)); // 传递值而非依赖上下文
-
使用托管线程服务(推荐)
@Resource private ManagedExecutorService managedExecutor; // WebSphere 托管managedExecutor.submit(() -> {// WebSphere 自动注入上下文Context ctx = new InitialContext(); // 现在正常工作了! });
-
手动恢复上下文(复杂)
// 在 Web 线程中捕获当前类加载器 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();new Thread(() -> {// 手动恢复类加载器Thread.currentThread().setContextClassLoader(originalClassLoader);// 注意:这无法恢复 JNDI 或 ServletContext! }).start();
总结
线程上下文丢失本质是:当代码脱离 Web 容器管控的线程时,应用服务器无法自动提供运行所需的环境信息。解决的关键在于:
-
避免在自定义线程中直接访问 Web API
-
使用服务器托管线程池(
ManagedExecutorService
) -
在 Web 线程中预先获取数据并传递值
这正是您遇到的 getStandardContext failed to retrieve application name
的根本原因——自定义线程中无法关联到 Web 应用的 ServletContext
。
===
■如果我想在servlet中使用线程,代码应该如何实现
在 Servlet 中使用自定义线程时,为了正确处理线程上下文问题(特别是在 WebSphere 环境中),强烈推荐使用 Java EE 标准的 ManagedExecutorService
。以下是完整的实现方案:
推荐方案:使用 ManagedExecutorService(WebSphere 托管线程池)
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.enterprise.concurrent.ManagedExecutorService;
import java.io.IOException;
import java.io.PrintWriter;@WebServlet("/asyncTask")
public class ThreadedServlet extends HttpServlet {// 注入 WebSphere 托管的线程池服务@Resourceprivate ManagedExecutorService managedExecutor;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/plain");PrintWriter out = response.getWriter();// 提交任务到托管线程池(自动传递线程上下文)managedExecutor.submit(() -> {try {// 在托管线程中安全访问上下文String appName = (String) new InitialContext().lookup("java:app/AppName");String contextPath = request.getServletContext().getContextPath();// 执行实际业务逻辑processTask(appName, contextPath);out.println("Task completed successfully in managed thread!");} catch (Exception e) {out.println("Error in managed thread: " + e.getMessage());e.printStackTrace();}});out.println("Background task started using ManagedExecutorService...");}private void processTask(String appName, String contextPath) {// 这里是实际的业务逻辑System.out.println("Processing task for application: " + appName);System.out.println("Context path: " + contextPath);// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
备选方案:手动管理线程上下文(如果无法使用 ManagedExecutorService)
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.naming.InitialContext;@WebServlet("/manualThread")
public class ManualThreadServlet extends HttpServlet {// 创建普通线程池(不推荐,仅作演示)private final ExecutorService executor = Executors.newFixedThreadPool(5);@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/plain");PrintWriter out = response.getWriter();// 在Web线程中预先获取所需上下文信息final String appName = getPredefinedAppName();final String contextPath = request.getServletContext().getContextPath();final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();// 提交任务到普通线程池executor.submit(() -> {// 保存原始类加载器(用于恢复)ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();try {// 手动设置上下文类加载器Thread.currentThread().setContextClassLoader(contextClassLoader);// 使用预先获取的上下文信息processTask(appName, contextPath);out.println("Task completed in manual thread!");} catch (Exception e) {out.println("Error in manual thread: " + e.getMessage());e.printStackTrace();} finally {// 恢复原始类加载器Thread.currentThread().setContextClassLoader(originalClassLoader);}});out.println("Background task started using manual thread...");}private String getPredefinedAppName() {try {// 在Web线程中预先获取应用名称return (String) new InitialContext().lookup("java:app/AppName");} catch (Exception e) {return "default-app";}}private void processTask(String appName, String contextPath) {// 业务逻辑同上}@Overridepublic void destroy() {// 关闭线程池executor.shutdown();super.destroy();}
}
关键配置步骤(WebSphere 控制台)
-
启用并发策略:
-
登录 WebSphere 管理控制台
-
导航到:资源 > 并发策略
-
创建或使用默认的并发策略
-
-
绑定到应用(可选,通常自动注入即可工作):
在ibm-application-bnd.xml
中添加:<application-bnd><managed-executor-service name="concurrent/executorSvc" /> </application-bnd>
两种方案对比
特性 | ManagedExecutorService | 手动线程管理 |
---|---|---|
上下文传播 | 自动完整传播(类加载器、JNDI、安全等) | 仅能手动传递类加载器 |
资源管理 | WebSphere 自动管理生命周期 | 需手动关闭线程池 |
事务支持 | 支持事务上下文传播 | 不支持事务传播 |
Servlet API 访问 | 可直接访问(如 request 、response ) | 只能访问预先获取的数据 |
WebSphere 兼容性 | 完全兼容 | 可能仍有上下文问题 |
代码复杂度 | 简单(声明式注入) | 复杂(需手动管理上下文) |
推荐度 | ⭐⭐⭐⭐⭐(首选方案) | ⭐⭐(备选方案) |
最佳实践建议
-
首选托管线程池:
@Resource private ManagedExecutorService executor; // 始终使用这个
-
避免在子线程中直接使用 Servlet API:
// 错误做法(可能引发问题): managedExecutor.submit(() -> {request.getSession(); // 可能不安全 });// 正确做法(预先获取所需数据): String sessionId = request.getSession().getId(); managedExecutor.submit(() -> processSession(sessionId));
-
处理线程中的异常:
managedExecutor.submit(() -> {try {// 业务逻辑} catch (Exception e) {// 1. 记录日志// 2. 不要吞掉异常// 3. 考虑通知主线程} });
-
生命周期管理:
-
不需要手动关闭
ManagedExecutorService
-
对于普通线程池,在
Servlet.destroy()
中关闭:@Override public void destroy() {executor.shutdown();try {if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {executor.shutdownNow();}} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();} }
-
完整示例(生产级代码)
@WebServlet("/task")
public class ProductionReadyServlet extends HttpServlet {@Resourceprivate ManagedExecutorService managedExecutor;private static final Logger logger = LoggerFactory.getLogger(ProductionReadyServlet.class);@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {// 预先获取所需上下文信息String appName = (String) req.getServletContext().getAttribute("app.name");String clientIP = req.getRemoteAddr();// 提交任务managedExecutor.submit(new TrackedTask(appName, clientIP));resp.getWriter().write("Task submitted successfully");}// 封装任务的内部类private class TrackedTask implements Runnable {private final String appName;private final String clientIP;TrackedTask(String appName, String clientIP) {this.appName = appName;this.clientIP = clientIP;}@Overridepublic void run() {MDC.put("app", appName); // 日志上下文MDC.put("client", clientIP);try {logger.info("Starting background task");// 业务逻辑...logger.info("Task completed");} catch (Exception e) {logger.error("Task failed", e);} finally {MDC.clear();}}}
}
使用这种模式,您可以:
-
安全地在后台线程执行任务
-
保留必要的上下文信息
-
获得完善的错误处理和日志跟踪
-
完全兼容 WebSphere 环境
-
避免
getStandardContext failed
错误
对于大多数场景,使用 ManagedExecutorService
并遵循上述模式即可解决线程上下文问题。
==
相关文章:
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
FTXUI::Dom 模块
DOM 模块定义了分层的 FTXUI::Element 树,可用于构建复杂的终端界面,支持响应终端尺寸变化。 namespace ftxui {...// 定义文档 定义布局盒子 Element document vbox({// 设置文本 设置加粗 设置文本颜色text("The window") | bold | color(…...
嵌入式面试常问问题
以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架
1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...
GeoServer发布PostgreSQL图层后WFS查询无主键字段
在使用 GeoServer(版本 2.22.2) 发布 PostgreSQL(PostGIS)中的表为地图服务时,常常会遇到一个小问题: WFS 查询中,主键字段(如 id)莫名其妙地消失了! 即使你在…...

VSCode 使用CMake 构建 Qt 5 窗口程序
首先,目录结构如下图: 运行效果: cmake -B build cmake --build build 运行: windeployqt.exe F:\testQt5\build\Debug\app.exe main.cpp #include "mainwindow.h"#include <QAppli...

Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
应用场景: 1、常规某个机器被钓鱼后门攻击后,我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后,我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解
文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema,不需要复杂的查询,只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 :在几秒钟…...
StarRocks 全面向量化执行引擎深度解析
StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计,相比传统行式处理引擎(如MySQL),性能可提升 5-10倍。以下是分层拆解: 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...
SQL进阶之旅 Day 22:批处理与游标优化
【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…...
深度解析云存储:概念、架构与应用实践
在数据爆炸式增长的时代,传统本地存储因容量限制、管理复杂等问题,已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性,成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理,云存储正重塑数据存储与…...
stm32进入Infinite_Loop原因(因为有系统中断函数未自定义实现)
这是系统中断服务程序的默认处理汇编函数,如果我们没有定义实现某个中断函数,那么当stm32产生了该中断时,就会默认跑这里来了,所以我们打开了什么中断,一定要记得实现对应的系统中断函数,否则会进来一直循环…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...

表单设计器拖拽对象时添加属性
背景:因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...
简单介绍C++中 string与wstring
在C中,string和wstring是两种用于处理不同字符编码的字符串类型,分别基于char和wchar_t字符类型。以下是它们的详细说明和对比: 1. 基础定义 string 类型:std::string 字符类型:char(通常为8位)…...

CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?
在现代前端开发中,Utility-First (功能优先) CSS 框架已经成为主流。其中,Tailwind CSS 无疑是市场的领导者和标杆。然而,一个名为 UnoCSS 的新星正以其惊人的性能和极致的灵活性迅速崛起。 这篇文章将深入探讨这两款工具的核心理念、技术差…...
Yii2项目自动向GitLab上报Bug
Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...
背包问题双雄:01 背包与完全背包详解(Java 实现)
一、背包问题概述 背包问题是动态规划领域的经典问题,其核心在于如何在有限容量的背包中选择物品,使得总价值最大化。根据物品选择规则的不同,主要分为两类: 01 背包:每件物品最多选 1 次(选或不选&#…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...

Qt的学习(二)
1. 创建Hello Word 两种方式,实现helloworld: 1.通过图形化的方式,在界面上创建出一个控件,显示helloworld 2.通过纯代码的方式,通过编写代码,在界面上创建控件, 显示hello world; …...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...

工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...

鸿蒙Navigation路由导航-基本使用介绍
1. Navigation介绍 Navigation组件是路由导航的根视图容器,一般作为Page页面的根容器使用,其内部默认包含了标题栏、内容区和工具栏,其中内容区默认首页显示导航内容(Navigation的子组件)或非首页显示(Nav…...
JavaScript 标签加载
目录 JavaScript 标签加载script 标签的 async 和 defer 属性,分别代表什么,有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...
「Java基本语法」变量的使用
变量定义 变量是程序中存储数据的容器,用于保存可变的数据值。在Java中,变量必须先声明后使用,声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例:声明与初始化 public class VariableDemo {publi…...

CMS内容管理系统的设计与实现:多站点模式的实现
在一套内容管理系统中,其实有很多站点,比如企业门户网站,产品手册,知识帮助手册等,因此会需要多个站点,甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...
用鸿蒙HarmonyOS5实现国际象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码,使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...