当前位置: 首页 > news >正文

【强化学习】gymnasium自定义环境并封装学习笔记

【强化学习】gymnasium自定义环境并封装学习笔记

  • gym与gymnasium简介
    • gym
    • gymnasium
  • gymnasium的基本使用方法
  • 使用gymnasium封装自定义环境
    • 官方示例及代码
    • 编写环境文件
      • __init__()方法
      • reset()方法
      • step()方法
      • render()方法
      • close()方法
  • 注册环境
    • 创建包 Package(最后一步)
    • 创建自定义环境示例
  • 参考文献

gym与gymnasium简介

gym

  • gym(OpenAI Gym)和gymnasium是两个不同的Python库,它们都旨在为强化学习研究提供环境和工具

  • gym出现的原因:不同于监督学习那样需要的是数据集,强化学习需要的是运行任务所需的环境,研究人员需要拥有标准化的环境和模块化的强化学习代码,方便复用以及方便研究人员能够在相同的环境和条件下测试算法

  • gym通过提供一个统一的接口,

  • gym(OpenAI Gym)是由OpenAI团队开发的,是最早和最广泛使用的强化学习环境库之一

  • 用于开发和比较强化学习算法的工具包和测试平台,提供了一个统一的接口来控制和交互各种环境

  • 截止2023年,Gym 已经不再更新或维护,最新版本为v0.26.2
    在这里插入图片描述

  • Gym的最新版本为v0.26.2,并且从这个版本开始,Gym的维护工作由Farama Foundation接手,并推出了Gymnasium

  • gym官网
    在这里插入图片描述

gymnasium

  • 所有Gym的开发工作已经转移到Gymnasium
  • gymnasium是一个较新的库,它试图解决gym中的一些限制和问题,并提供更现代化的接口
  • gymnasium设计时考虑了与gym的兼容性。它提供了一个兼容层,使得大多数gym环境可以直接在gymnasium中使用,无需或只需很少的修改
  • gymnasium官网
    在这里插入图片描述

gymnasium的基本使用方法

暂时先略过,日后补上,先介绍gymnasium封装自定义环境


使用gymnasium封装自定义环境

  • gymnasium官方介绍封装自定义环境的文档,本文主要基于此文档
  • 官方提供了示例代码,链接在此
  • 安装gymnasium命令:
pip install gymnasium

官方示例及代码

  • 官方使用的示例代码结构,如下所示
    在这里插入图片描述
  • wrappers是指包装器,用于修改或增强现有环境的行为,而不需要直接修改环境的源代码
  • 使用 wrappers 的一个关键优势是它们提供了一种灵活的方式来修改和扩展环境的功能,而不需要改变环境本身的实现。这使得研究人员可以专注于算法的开发,同时利用 wrappers 来适应不同的实验条件和研究目标。
  • env文件夹下的文件是环境名字

在这里插入图片描述

  • 在命令行中可使用tree 命令查看目录及文件结构,windows下需要使用/F参数来显示文件
tree /F C:\path\to\directory

在这里插入图片描述

编写环境文件

  • 所有自定义环境必须继承抽象类gymnasium.Env
  • 同时需要定义metadata,在 Gym 环境中,metadata 字典包含了环境的元数据,这些数据提供了关于环境行为和特性的额外信息

“render_modes”: 这个键的值是一个列表,指明了环境支持的渲染模式。在这个例子中,环境支持两种渲染模式:
“human”: 这种模式通常是指在屏幕上以图形界面的形式渲染环境,适合人类观察者观看。
“rgb_array”: 这种模式下,环境的渲染结果会以 RGB 数组的形式返回,这可以用于机器学习算法的输入,或者进行进一步的处理和分析。
“render_fps”: 这个键表示环境渲染的帧率,即每秒钟可以渲染的帧数。在这个例子中,4 表示环境将以每秒 4 帧的速率进行渲染。这通常用于控制渲染速度,使动画的播放更加平滑或符合特定的显示需

在这里插入图片描述

  • 在环境文件中需要实现__init__(),reset().setp(),render(),close()等方法,确保环境能够按照强化学习的标准工作流程运行
  • 定义action_space,智能体可以执行的动作类型和范围;定义observation_space,智能体可以观察到的状态的类型和范围
  • from gymnasium import spaces
  • 连续的空间使用spaces.Box 定义,low 和 high 参数指定了取值范围。
  • 离散的空间使用spaces.Discrete,参数指定可能的数量

init()方法

  • 初始化方法,用于设置环境的初始状态。这里可以定义环境参数、初始化状态空间和动作空间等
  • 定义 action_space 和 observation_space时,需要从 Gymnasium 的 spaces 模块导入spaces
  • spaces 模块提供了多种空间类型,用于表示强化学习环境中可能的动作和观察的类型和结构
    在这里插入图片描述
import numpy as np
import pygameimport gymnasium as gym
from gymnasium import spacesclass GridWorldEnv(gym.Env):metadata = {"render_modes": ["human", "rgb_array"], "render_fps": 4}def __init__(self, render_mode=None, size=5):self.size = size  # The size of the square gridself.window_size = 512  # The size of the PyGame window# Observations are dictionaries with the agent's and the target's location.# Each location is encoded as an element of {0, ..., `size`}^2, i.e. MultiDiscrete([size, size]).self.observation_space = spaces.Dict({"agent": spaces.Box(0, size - 1, shape=(2,), dtype=int),"target": spaces.Box(0, size - 1, shape=(2,), dtype=int),})# We have 4 actions, corresponding to "right", "up", "left", "down"self.action_space = spaces.Discrete(4)"""The following dictionary maps abstract actions from `self.action_space` tothe direction we will walk in if that action is taken.I.e. 0 corresponds to "right", 1 to "up" etc."""self._action_to_direction = {0: np.array([1, 0]),1: np.array([0, 1]),2: np.array([-1, 0]),3: np.array([0, -1]),}assert render_mode is None or render_mode in self.metadata["render_modes"]self.render_mode = render_mode"""If human-rendering is used, `self.window` will be a referenceto the window that we draw to. `self.clock` will be a clock that is usedto ensure that the environment is rendered at the correct framerate inhuman-mode. They will remain `None` until human-mode is used for thefirst time."""self.window = Noneself.clock = None

reset()方法

  • 用于重置环境状态,在每个训练周期(episode)开始时,reset() 方法被调用以重置环境到一个初始状态
  • 每次训练周期结束并且接收到结束信号(done 标志)时,会调用 reset 方法来重置环境状态
  • 用户可以通过 reset 方法传递一个 seed 参数,用于初始化环境使用的任何随机数生成器,确保环境行为的确定性和可复现性
def reset(self, seed=None, options=None):# We need the following line to seed self.np_randomsuper().reset(seed=seed)# Choose the agent's location uniformly at randomself._agent_location = self.np_random.integers(0, self.size, size=2, dtype=int)# We will sample the target's location randomly until it does not coincide with the agent's locationself._target_location = self._agent_locationwhile np.array_equal(self._target_location, self._agent_location):self._target_location = self.np_random.integers(0, self.size, size=2, dtype=int)observation = self._get_obs()info = self._get_info()if self.render_mode == "human":self._render_frame()return observation, info

step()方法

  • step()方法是环境与智能体交互的核心,包含了环境逻辑的核心部分
  • step()方法处理动作,更新环境状态,并返回五个值组成的元组(observation, reward, terminated, truncated, info):观察(observation)、奖励(reward)、是否终止(terminated)、是否截断(truncated)和附加信息(info)

五元组的含义(observation, reward, terminated, truncated, info)

观察(Observation):这是环境状态的表示,智能体根据这个观察来选择动作。观察可以是状态的一部分或全部,也可以是经过加工的信息,如图像、向量等。观察是智能体与环境交互的直接输入。
奖励(Reward):这是一个标量值,表示智能体执行动作后从环境中获得的即时反馈。奖励用于指导智能体学习哪些行为是好的,哪些是不好的。在许多任务中,智能体的目标是最大化其获得的总奖励。
是否终止(Terminated/Done):这是一个布尔值,表示当前周期(episode)是否结束。如果为 True,则表示智能体已经完成了任务,或者环境已经达到了一个终止状态,智能体需要重新开始新的周期。
是否截断(Truncated):这也是一个布尔值,与 done 相似,但表示周期结束的原因可能不是任务完成,而是其他原因,如超时、达到某个特定的中间状态或违反了某些规则。在某些实现中,truncated 可能与 done 相同或不被使用。
附加信息(Info):这是一个字典,包含除观察、奖励、终止和截断之外的额外信息。这些信息可以包括关于状态转换的元数据,如是否处于探索阶段、环境的内部计数器、额外的性能评估指标等。

def step(self, action):# Map the action (element of {0,1,2,3}) to the direction we walk indirection = self._action_to_direction[action]# We use `np.clip` to make sure we don't leave the gridself._agent_location = np.clip(self._agent_location + direction, 0, self.size - 1)# An episode is done iff the agent has reached the targetterminated = np.array_equal(self._agent_location, self._target_location)reward = 1 if terminated else 0  # Binary sparse rewardsobservation = self._get_obs()info = self._get_info()if self.render_mode == "human":self._render_frame()return observation, reward, terminated, False, info
  • info可通过_get_info方法获取,该方法用于收集和返回除了观察和奖励之外的其他有用信息。这些信息可以包括关于环境状态的额外数据
  • _get_obs方法负责将环境的内部状态转换为智能体可以观察的形式,通常涉及到从环境状态中提取相关信息,并将其格式化为智能体能够理解和使用的数据结构
def _get_obs(self):return {"agent": self._agent_location, "target": self._target_location}
def _get_info(self):return {"distance": np.linalg.norm(self._agent_location - self._target_location, ord=1)}

render()方法

  • render 方法用于将环境的状态可视化
  • 使用 Gymnasium 创建自定义环境时,PyGame 是一种流行的库,用于渲染环境的视觉表示。PyGame 允许创建图形窗口,并将环境的状态绘制到屏幕上,这对于需要视觉反馈的强化学习任务非常有用

渲染模式:

  • “human”:以图形界面的形式渲染,适用于人类观察者。
  • “rgb_array”:返回一个 RGB 图像数组,可以用于机器学习模型或进一步处理。
  • 下面为示例代码中的render方法
def render(self):if self.render_mode == "rgb_array":return self._render_frame()def _render_frame(self):if self.window is None and self.render_mode == "human":pygame.init()pygame.display.init()self.window = pygame.display.set_mode((self.window_size, self.window_size))if self.clock is None and self.render_mode == "human":self.clock = pygame.time.Clock()canvas = pygame.Surface((self.window_size, self.window_size))canvas.fill((255, 255, 255))pix_square_size = (self.window_size / self.size)  # The size of a single grid square in pixels# First we draw the targetpygame.draw.rect(canvas,(255, 0, 0),pygame.Rect(pix_square_size * self._target_location,(pix_square_size, pix_square_size),),)# Now we draw the agentpygame.draw.circle(canvas,(0, 0, 255),(self._agent_location + 0.5) * pix_square_size,pix_square_size / 3,)# Finally, add some gridlinesfor x in range(self.size + 1):pygame.draw.line(canvas,0,(0, pix_square_size * x),(self.window_size, pix_square_size * x),width=3,)pygame.draw.line(canvas,0,(pix_square_size * x, 0),(pix_square_size * x, self.window_size),width=3,)if self.render_mode == "human":# The following line copies our drawings from `canvas` to the visible windowself.window.blit(canvas, canvas.get_rect())pygame.event.pump()pygame.display.update()# We need to ensure that human-rendering occurs at the predefined framerate.# The following line will automatically add a delay to keep the framerate stable.self.clock.tick(self.metadata["render_fps"])else:  # rgb_arrayreturn np.transpose(np.array(pygame.surfarray.pixels3d(canvas)), axes=(1, 0, 2))

close()方法

  • close 方法用于在环境不再使用时进行清理操作,例如关闭图形界面窗口、释放资源或执行其他必要的清理任务
  • 是一个没有参数也没有返回值的方法
  • 如果环境使用 PyGame 或其他图形库创建了渲染窗口,close 方法应该关闭这些窗口。
def close(self):if self.window is not None:pygame.display.quit()pygame.quit()

注册环境

  • 编写完上述与环境相关的代码后,需要注册自定义环境
  • 注册自定义环境是为了使gymnasium检测到该环境
from gymnasium.envs.registration import registerregister(id="gym_examples/GridWorld-v0",entry_point="gym_examples.envs:GridWorldEnv",max_episode_steps=300,
)
  • environment ID由三部分组成,①命名空间gym_examples(可选) ②强制名称GridWorld ③版本v0(可选)
  • entry_point参数在注册自定义环境时使用,它指定了如何导入这个环境类
  • 格式通常是module:classname
  • module 是包含环境类的 Python 模块的路径。
    classname 是环境中具体的类的名称
  • 其他可指定的参数如下所示:

在这里插入图片描述

  • 经过注册的自定义环境GridWorldEnv可由以下命令创建
env = gymnasium.make('gym_examples/GridWorld-v0')
  • gym-examples/gym_examples/envs/init.py 文件中需要包含以下的内容
from gym_examples.envs.grid_world import GridWorldEnv

创建包 Package(最后一步)

  • 将代码构建为python的包,方便地在不同项目中重用自定义的环境代码
  • gym-examples/setup.py中写入以下内容
from setuptools import setupsetup(name="gym_examples",version="0.0.1",install_requires=["gymnasium==0.26.0", "pygame==2.1.0"],
)

此处可以将"==“改为”>=",如果不打算做图形化可以删去pygame==2.1.0

安装自定义环境(在包含 setup.py 的目录中执行)

pip install -e .
  • 安装成功后会生成gym_examples.egg-info文件夹

创建自定义环境示例

  • 使用以下命令
import gym_examples
env = gymnasium.make('gym_examples/GridWorld-v0')
  • 传参的版本
import gym_examples
env = gymnasium.make('gym_examples/GridWorld-v0', size=10)

参考文献

  1. Gymnasium Documentation:Make your own custom environment
  2. 深度强化学习:gymnasium下创建自己的环境(保姆式教程)

相关文章:

【强化学习】gymnasium自定义环境并封装学习笔记

【强化学习】gymnasium自定义环境并封装学习笔记 gym与gymnasium简介gymgymnasium gymnasium的基本使用方法使用gymnasium封装自定义环境官方示例及代码编写环境文件__init__()方法reset()方法step()方法render()方法close()方法 注册环境创建包 Package(最后一步&a…...

TLE9879的基于Arduino调试板SWD刷写接口

官方的Arduino评估板,如下图所示: 如果你有官方的调试器,应该不用关注本文章,如下图连接就是: 如果,您和博主一样需要自己飞线的话,如下图所示:PCB的名称在右边整理,SWD的…...

基于 Delphi 的前后端分离:之五,使用 HTMX 让页面元素组件化之面向对象的Delphi代码封装

前情提要 本博客上一篇文章,描述了使用 Delphi 作为后端的 Web Server,前端使用 HTMX 框架,把一个开源的前端图表 JS 库,进行了组件化。 上一篇文章仅仅是描述了简单的前端代码组件化的可能性,依然是基于前端库的 JS…...

讲透计算机网络知识(实战篇)01——计算机网络和协议

一、计算机网络和协议 1、网络和互联网络 1.1 网络、互联网、Internet 用交换机、集线器连接在一起的计算机构成一个网络。 用路由器连接多个网络,形成互联网。 全球最大的互联网:Internet。 1.2 网络举例 家庭互联网 图中的无线拨号路由器既是路由…...

8个宝藏APP,个个都牛逼哈拉!

AI视频生成:小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 目前win7已经逐渐淡出人们的视野,大部分人都开始使用win10,在日常工作和使用中,创客们下载神奇的软件能大幅提…...

使用docker构建java应用

1、docker简介 Docker是一个开源的容器化平台,可以帮助开发人员将应用程序及其依赖项打包成一个可移植的容器。容器化是一种轻量级的虚拟化技术,可以使应用程序在不同的操作系统和环境中具有一致的运行方式。 使用Docker带来的好处包括: 简…...

Oracle 存储过程

Oracle存储过程 创建存储过程 CREATE OR REPLACE PROCEDURE UPDATE_EMPLOYEE_SALARY(p_employee_id IN NUMBER,p_employee_salary IN NUMBER )AS BEGINUPDATE employeesSET salary p_employee_salaryWHERE employee_id p_employee_id;COMMIT;EXCEPTIONWHEN NO_DATA_FOUND T…...

下载站名文件

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 得到了请求地址与请求参数后,可以发现请求参数中的出发地与目的地均为车站名的英文缩写。而这个英文缩写的字母是通过输入中文车站名转换…...

345453

38744...

Java操作redis

目录 一:Jedis 二:使用Spring Data Redis Redis 的 Java 客户端很多,官方推荐的有三种: 1.Jedis 2.Lettuce 3.Redisson 同时,Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在S…...

【数据结构(邓俊辉)学习笔记】图03——拓扑排序

文章目录 0. 概述1. 零入度算法1. 1 拓扑排序1. 2 算法 2. 零出度算法2.1 算法2.2 实现2.3. 复杂度 0. 概述 学习下拓扑排序 1. 零入度算法 1. 1 拓扑排序 首先理解下拓扑排序 其实老师经常干这事,如编讲义,将已经知道的知识点串起来变成讲课序列。那…...

C#参数使用场景简要说明

C#参数使用场景简要说明 1、传值参数 方法、类成员的初始化 2、输出参数 方法返回值不能满足,需要多个返回值时; 3、引用参数 方法需要修改变量需带回原变量时; 4、具名参数 代码可读性高,参数可交换位置 5、方法扩展&#xff08…...

线性代数|机器学习-P10最小二乘法的四种方案

文章目录 1. 概述2. SVD奇异值分解3. 最小二乘法方程解4. 最小二乘法图像解释5. Gram-Schmidt 1. 概述 当我们需要根据一堆数据点去拟合出一条近似的直线的时候,就会用到 最小二乘法 .根据矩阵A的情况,有如下四种方法 在r n m 时,SVD奇异…...

【Android面试八股文】你能描述一下JVM中的类加载过程吗?

文章目录 一、Java类的生命周期二、JVM类加载过程1. 加载(Loading)2. 链接(Linking)a. 验证(Verification)b. 准备(Preparation)b.1 准备阶段的初始值b.2 用户定义的初值b.3 常量的初始化c. 解析(Resolution)3. 初始化(Initialization)3.1 什么是 `<clinit>`…...

MYSQL八、MYSQL的SQL优化

一、SQL优化 sql优化是指&#xff1a;通过对sql语句和数据库结构的调整&#xff0c;来提高数据库查询、插入、更新和删除等操作的性能和效率。 1、插入数据优化 要一次性往数据库表中插入多条记录&#xff1a; insert into tb_test values(1,tom); insert into tb_tes…...

鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS

1、LFS文件系统结构体介绍 会分2部分来介绍结构体部分&#xff0c;先介绍LittleFS文件系统的结构体&#xff0c;然后介绍LiteOS-M内核中提供的和LittleFS相关的一些结构体。 1.1 LittleFS的枚举结构体 在openharmony/third_party/littlefs/lfs.h头文件中定义LittleFS的枚举、…...

【ARMv8/ARMv9 硬件加速系列 3 -- SVE 指令语法及编译参数详细介绍】

文章目录 SVE 汇编语法SVE 单通道谓词SVE 测试代码 SVE 软件和库支持SVE 编译参数配置-marcharmv8-alseprofilememtagsve2-aessve2-bitpermcryptosve2sve2-sha3sve2-sm4 SVE 汇编语法 在介绍 SVE 汇编指令语法之前&#xff0c;先介绍下如何判断自己所使用的芯片是否实现了SVE功…...

Java版+ SaaS应用+接口技术RESTful API 技术开发的智慧医院HIS系统源码 专注医院管理系统研发 支持二开

Java版 SaaS应用接口技术RESTful API WebSocket WebService技术开发的智慧医院HIS系统源码 专注医院管理系统研发 支持二开 医院住院管理系统&#xff08;Hospital Information System简称HIS&#xff09;是一门医学、信息、管理、计算机等多种学科为一体的边缘科学&#xff…...

工业机器人远程运维,增强智慧工厂运营管理

1、需求背景 随着工业自动化技术的普及和工业机器人应用的增加&#xff0c;制造业对于生产线稳定性和效率的要求不断提高。然而&#xff0c;传统的现场监控方式存在着地理位置限制、实时监控难度大以及诊断能力有限等问题&#xff0c;迫切需要一种更具灵活性和效率的监控方式。…...

理解Python的元类

1.type()函数 type 函数是一个内置函数&#xff0c;用来获取一个对象的类型。它可以接受一个参数&#xff0c;返回这个参数的数据类型。type也可以用来创建类&#xff0c;type就是元类 x333 list["ab"] tuple (1, "a", True, 3.14) dict {name: Alice,…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...

Windows 下端口占用排查与释放全攻略

Windows 下端口占用排查与释放全攻略​ 在开发和运维过程中&#xff0c;经常会遇到端口被占用的问题&#xff08;如 8080、3306 等常用端口&#xff09;。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口&#xff0c;帮助你高效解决此类问题。​ 一、准…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...

C#中用于控制自定义特性(Attribute)

我们来详细解释一下 [AttributeUsage(AttributeTargets.Class, AllowMultiple false, Inherited false)] 这个 C# 属性。 在 C# 中&#xff0c;Attribute&#xff08;特性&#xff09;是一种用于向程序元素&#xff08;如类、方法、属性等&#xff09;添加元数据的机制。Attr…...

JUC并发编程(二)Monitor/自旋/轻量级/锁膨胀/wait/notify/锁消除

目录 一 基础 1 概念 2 卖票问题 3 转账问题 二 锁机制与优化策略 0 Monitor 1 轻量级锁 2 锁膨胀 3 自旋 4 偏向锁 5 锁消除 6 wait /notify 7 sleep与wait的对比 8 join原理 一 基础 1 概念 临界区 一段代码块内如果存在对共享资源的多线程读写操作&#xf…...