并发编程基本设计方案 线程本地存储模式(没有共享,就没有伤害)


上篇我们提到,并发编程中,并发问题之所以会产生,是因为多个线程对一个资源进行读写操作,即共享 + 读写,在上篇中,我们了解到不可变模式通过破坏读写这个条件,避免的并发问题的产生,那么今天我们要讲的是通过限制共享,来避免并发问题的产生。


在讲之前,我们先来补一补JVM的知识,我们知道JVM有三大子系统,类加载器,字节码执行引擎,以及运行时数据区,而今天我们来了解一下运行时数据区,我们从线程的视角来看看运行时数据区究竟有什么?在线程的眼中,运行时数据区,可以被分为两块区域,一块是我自己独享的,一块是我和其他线程共享的,独享的区域包括 虚拟机栈,本地方法栈,程序计数器,共享的区域包括 方法区(元数据区)堆内存。简单介绍一下这些区域的作用,方法的调用需要借助栈这种数据结构,Java这门编程语言是可以借助一些机制调用本地方法,就是Native Method,就是用C、C++写的函数,于是栈就被分为了两个,虚拟机栈主要就是用于维护Java方法的调用关系,而本地方法栈就是维护Native Method的调用关系,程序计数器就是记录当前执行到整个流程中的哪一步了。方法区(元数据区)记录了类的元信息,比如类的名字,类的字段,类的方法,堆内存就是存储对象实例的地方。


好,知识补充到此为止,到此,你已经知道了,一个线程的创建,必然会伴随着虚拟机栈,本地方法栈以及程序计数器的分配,而并发问题产生的一个条件就是,多个线程在共享一块数据,就是同一块内存区域,那么今天所说的限制共享,就是将数据移动到线程的独享区中,这样其他线程就无法访问了,从而避免的并发问题的产生。线程是执行方法的直接载体,将数据移动到线程的独享区,对应到代码中,就是将数据移入到局部变量中,这样其他线程就访问不到,限制了共享。但是这种方法存在比较严重的问题,那就是对象的重复创建,比如,有10个线程,调用A方法3次,A方法中利用局部变量保存一个对象,随着线程的执行,对象就是被重复创建,创建的个数与调用次数成正比,这对于JVM来说,是难以接受的。于是我们可以放开一点限制,数据不一定要保存在局部变量中,我们的核心目的是在线程层面,数据独享,为了达到这个目的,Java给我们提供了一个ThreadLocal ,这个工具类可以帮我们把数据维护成线程独享。但是值得注意的是,ThreadLocal仅仅只是一个工具类,并不负责数据的持有,真正持有数据的其实是Thread


public T get() {
//这里就可以证明,数据其实是被Thread持有
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}


对于ThreadLocal具体怎么用,网上已经有很多教程了,你也可以尝试看看源码,至于ThreadLocal的缺点,我们下期来简单聊聊。


0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
pandaer
作者分享
#AI应用开发 springAI是如何实现mcp的工具调用的,在springai中,工具调用被抽象成了一个toolcallback,无论是我们自己写的工具调用,还是mcp提供的,最终都会变成这个类的实例,然后再大模型返回的结果里,如果有工具调用,就会调用toolcallback的call方法,来执行真正的工具调用
4
#AI应用开发 今天学习了如何利用springAI来开发mcp server以及mcp client。开发mcp client很简单,只需要引入依赖,配置一下就好。开发一个mcp server也是一样 先要引入一个依赖,然后手动注入toolcallbackprovider这个bean就行了。需要利用tool注解来自定义需要对外提供的工具
4
#AI应用开发 今天学习了toolcall,本质还是给大模型提供上下文信息,让其生成调用。在传递给大模型的消息中有一个额外的字段叫做tools,里面定义了工具的声明。在模型的响应中有一个字段叫做tool_calls里面定义了要调用哪些工具。 工具的定义要清晰明了,参数结构尽可能的简单,不要试图在一次请求中拼装复杂的工具链。满足需求的情况下,越简单,越清晰越好
4
#AI应用开发 springAI实现短期记忆(会话内记忆)的实现是基于chatmemory来实现的,chatmemory这个接口专注于我要把那些消息交给大模型,而chatmemoryrepostory 这个接口专注的是会话中产生的消息怎么存? 存哪里? 是数据库还是内存?
4
#AI应用开发 springAI中 目前学习了两套api,一个是chatmodel,一个是chatclient。 chatmodel是封装了大模型提供商的chat请求,让我们可以很方便的调用模型,而不需要手写http请求。他提供了两个方法 一个是call,一个是stream 对应chat请求的两套模式,一个是非流式输出,一个是流式输出。所以这个更像是对大模型的抽象。 chatclient是基于chatmodel的封装,提供了更多的api,方便我们快速搭建相关的功能。 springAI 提供了prompt来抽象给chat的请求内容,不仅仅包含消息列表,还有一些模型配置,但是我感觉prompt这个名字不好,我宁愿叫chatrequest,也不叫prompt,因为一段小文本就是prompt
3
下载 APP