add kownledge ahout mechine learning2
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
# Golang
|
||||
|
||||
> 来源:原 mongodb 集合 `t_blog` · 整理日期:2026-08-25
|
||||
> 分类 ID:`2023022315480800000000000001` · 排序:12 · 文章数:4
|
||||
|
||||
## 目录
|
||||
|
||||
1. [3. grpc xds](#3-grpc-xds)
|
||||
2. [2. grpc health check](#2-grpc-health-check)
|
||||
3. [1. grpc protoc](#1-grpc-protoc)
|
||||
4. [4. Go](#4-go)
|
||||
|
||||
---
|
||||
|
||||
## 1. 3. grpc xds
|
||||
|
||||
<sub>bid: `2023022315480900000000000020`</sub>
|
||||
|
||||
该写点什么么...
|
||||
|
||||
---
|
||||
|
||||
## 2. 2. grpc health check
|
||||
|
||||
<sub>bid: `2023022315484500000000000019`</sub>
|
||||
|
||||
##
|
||||
|
||||
---
|
||||
|
||||
## 3. 1. grpc protoc
|
||||
|
||||
<sub>bid: `2023022315507600000000000021`</sub>
|
||||
|
||||
## base consepts
|
||||
|
||||
- protobuf
|
||||
|
||||
我们可以查看官方文档 [protobuf guides](https://protobuf.dev/programming-guides/proto3/)
|
||||
|
||||
简单来说就是一种以 .proto 结尾文件,遵循protocol buffer language 语法,其中包含一个或者多个message和service。
|
||||
|
||||
**notice_service.proto**
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package notice.service.v1;
|
||||
|
||||
option go_package ="violin-notice/pkg/service/notice.service.v1";
|
||||
|
||||
message NoticeMessage {
|
||||
|
||||
};
|
||||
|
||||
message NoticeResponse {};
|
||||
|
||||
service NoticeService {
|
||||
rpc SendNotice(NoticeMessage) returns (NoticeResponse) {}
|
||||
}
|
||||
|
||||
- protoc
|
||||
[protoc guides](https://github.com/bufbuild/protoc-gen-validate)
|
||||
|
||||
作为可执行程序,其作用是将上面的我们定义的.proto文件转换成对应语言的grpc 可用的文件,我们通过对这些文件进行扩展来实现我们想要的场景。
|
||||
|
||||
protoc --go_out=./gen --go_opt=paths=source_relative --go-grpc_out=./gen --go-grpc_opt=paths=source_relative notice_service.proto
|
||||
|
||||
执行上面命令会生成go语言的两个文件
|
||||
|
||||
notice_service_grpc.pb.go
|
||||
notice_service.pb.go
|
||||
|
||||
我们只需要自己写一个notice_service.go 来扩展上述两个文件即可。
|
||||
|
||||
**package notice_service_v1**
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gopkg.in/gomail.v2"
|
||||
"log"
|
||||
"violin-home.cn/common/logs"
|
||||
)
|
||||
|
||||
type NoticeService struct {
|
||||
UnimplementedNoticeServiceServer
|
||||
}
|
||||
|
||||
func New() *NoticeService {
|
||||
return &NoticeService{}
|
||||
}
|
||||
|
||||
|
||||
func (*NoticeService) SendNotice(
|
||||
ctx context.Context, msg *NoticeMessage) (*NoticeResponse, error) {
|
||||
|
||||
// to write logic
|
||||
return &NoticeResponse{}, nil
|
||||
}
|
||||
|
||||
|
||||
## 如何使用grpc
|
||||
|
||||
grpc 存在client和server
|
||||
|
||||
---
|
||||
|
||||
## 4. 4. Go
|
||||
|
||||
<sub>bid: `2023030213489100000000000022`</sub>
|
||||
|
||||
## 1. 概述
|
||||
一些简单的概念介绍可以通过下面网站学习到。
|
||||
[golang 菜鸟教程](https://www.runoob.com/go/go-tutorial.html)
|
||||
|
||||
## 2. package
|
||||
|
||||
golang的包管理模式是树形的,并且有以下规约:
|
||||
**1**
|
||||
树形结构的根节点是main,即项目根目录。
|
||||
**2**
|
||||
文件中的包定义 一定是和该文件所处的文件夹相同
|
||||
**3**
|
||||
文件中定义的函数,如果是像作为第三方包引用,函数的首字母必须大写
|
||||
|
||||
# violin-controller 工程为例 https://github.com/simple321vip/violin-controller
|
||||
ls
|
||||
main.go controller.go controller_test.go 都是 根目录下的文件,所以他们的package 都是 main
|
||||
请注意 包 的名字只能是该文件夹的名字,并不包括父级路径
|
||||
|
||||
**4 引包**
|
||||
引包 包括第三方包 和 项目自定义包
|
||||
在mod模式下,第三方包存在 \$GOPATH/pkg/mod
|
||||
而自定义包 引入的时候直接从采用 项目相对路径的方式引入
|
||||
|
||||
## 3. 变量与指针
|
||||
|
||||
变量分为 值变量和指针变量
|
||||
|
||||
## 4. 函数
|
||||
|
||||
**1. main()** 函数
|
||||
|
||||
程序入口函数
|
||||
|
||||
**2. init()** 函数
|
||||
|
||||
每一个源文件都可以包含一个init函数,init意为初始化,也表明init函数会在main函数执行之前执行。
|
||||
|
||||
**3. go func()** 函数
|
||||
|
||||
并发编程 即异步
|
||||
|
||||
**4. func() {}()** 匿名函数
|
||||
|
||||
匿名函数可以通过变量指向匿名函数,可以匿名函数直接执行
|
||||
|
||||
// 声明一个函数变量
|
||||
f := func() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// 调用函数
|
||||
f()
|
||||
|
||||
// 也可以直接执行匿名函数
|
||||
func() int {
|
||||
return 1
|
||||
}()
|
||||
|
||||
**5. func sample() func() int {}** 通过匿名函数实现闭包
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func getSequence() func() int {
|
||||
i:=0
|
||||
return func() int {
|
||||
i+=1
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
func main(){
|
||||
/* nextNumber 为一个函数,函数 i 为 0 */
|
||||
nextNumber := getSequence()
|
||||
|
||||
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
|
||||
fmt.Println(nextNumber())
|
||||
fmt.Println(nextNumber())
|
||||
fmt.Println(nextNumber())
|
||||
|
||||
/* 创建新的函数 nextNumber1,并查看结果 */
|
||||
nextNumber1 := getSequence()
|
||||
fmt.Println(nextNumber1())
|
||||
fmt.Println(nextNumber1())
|
||||
}
|
||||
|
||||
---
|
||||
@@ -0,0 +1,659 @@
|
||||
# Java and Spring
|
||||
|
||||
> 来源:原 mongodb 集合 `t_blog` · 整理日期:2026-08-25
|
||||
> 分类 ID:`00000000000002` · 排序:1 · 文章数:14
|
||||
|
||||
## 目录
|
||||
|
||||
1. [Springboot AbstractApplicationContext](#springboot-abstractapplicationcontext)
|
||||
2. [Map Set List](#map-set-list)
|
||||
3. [Java8 AQS](#java8-aqs)
|
||||
4. [Java8 Stream](#java8-stream)
|
||||
5. [Spring superinterface](#spring-superinterface)
|
||||
6. [Springboot DefaultSingletonBeanRegistry](#springboot-defaultsingletonbeanregistry)
|
||||
7. [Springboot SpringApplication](#springboot-springapplication)
|
||||
8. [Springboot AbstractBeanFactory](#springboot-abstractbeanfactory)
|
||||
9. [Springboot AbstractAutowireCapableBeanFactory](#springboot-abstractautowirecapablebeanfactory)
|
||||
10. [Springboot AbstractBeanDefinition](#springboot-abstractbeandefinition)
|
||||
11. [Springboot ConfigurationClassPostProcessor](#springboot-configurationclasspostprocessor)
|
||||
12. [Spring ASM](#spring-asm)
|
||||
13. [PostProcessorRegistrationDelegate](#postprocessorregistrationdelegate)
|
||||
14. [Spring Aop](#spring-aop)
|
||||
|
||||
---
|
||||
|
||||
## 1. Springboot AbstractApplicationContext
|
||||
|
||||
<sub>bid: `00000000000003`</sub>
|
||||
|
||||
## AbstractApplicationContext
|
||||
|
||||
AbstractApplicationContext 是 SpringBoot的 核心应用上下文父类。
|
||||
|
||||
## x.1 properties
|
||||
|
||||
### beanFactoryPostProcessors
|
||||
|
||||
### environment
|
||||
|
||||
## x.2 methods
|
||||
|
||||
### refresh
|
||||
|
||||
refresh 是 Springboot启动中核心方法。
|
||||
|
||||
- prepareRefresh
|
||||
- obtainFreshBeanFactory
|
||||
- prepareBeanFactory
|
||||
- postProcessBeanFactory
|
||||
- invokeBeanFactoryPostProcessors
|
||||
- registerBeanPostProcessors
|
||||
- onRefresh
|
||||
- finishBeanFactoryInitialization
|
||||
- finishRefresh
|
||||
|
||||
---
|
||||
|
||||
## 2. Map Set List
|
||||
|
||||
<sub>bid: `2022051816085000000000000006`</sub>
|
||||
|
||||
### 1. Summary
|
||||
|
||||
我们在开发过程在,最常见的三种数据存储结构就是 Map Set 和List了
|
||||
|
||||
这三种结构最明显的特征就是Map 是键值,Set是对象存储唯一,List是有序数组
|
||||
|
||||
|
||||
### Map<K, V>
|
||||
|
||||
键值存储,主键不允许重复
|
||||
|
||||
添加元素 使用put方法,如果key重复,则value被覆盖
|
||||
|
||||
### Set<E>
|
||||
|
||||
对象存储,对象不允许重复
|
||||
|
||||
/**
|
||||
* if this set did not already contain the specified element will return {@code true}
|
||||
* f this set already contains the element, the call leaves the set
|
||||
* unchanged and returns {@code false}
|
||||
*/
|
||||
public boolean add(E e)
|
||||
|
||||
### List<E>
|
||||
|
||||
list是由数组构成的,这就意味着,list是有顺序的,因为数组在内存中是一段连续的内存。
|
||||
|
||||
---
|
||||
|
||||
## 3. Java8 AQS
|
||||
|
||||
<sub>bid: `2022052314143400000000000014`</sub>
|
||||
|
||||
## AQ
|
||||
|
||||
---
|
||||
|
||||
## 4. Java8 Stream
|
||||
|
||||
<sub>bid: `2022060810550500000000000022`</sub>
|
||||
|
||||
## S
|
||||
|
||||
---
|
||||
|
||||
## 5. Spring superinterface
|
||||
|
||||
<sub>bid: `2022120915183100000000000001`</sub>
|
||||
|
||||
## summary
|
||||
|
||||
|
||||
## 1. Aware(感知)
|
||||
|
||||
Spring Ioc 是解耦行为,我们的业务代码其实是没有感知或者意识到spring框架的存在。如果我们需要使用spring Ioc中的资源时候,我们就需要有意识的去获取这个资源,而Aware 就是这样的存在。
|
||||
|
||||
@Service
|
||||
public class AppContextAware implements ApplicationContextAware {
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext=applicationContext;
|
||||
}
|
||||
|
||||
public void hello(){
|
||||
//user为容器中存在的bean
|
||||
User user = applicationContext.getBean("user", User.class);
|
||||
System.out.println(user);
|
||||
|
||||
//获取容器的环境、User.name为设置好的属性
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
String property = environment.getProperty("User.name");
|
||||
System.out.println(" 属性:"+property);
|
||||
}
|
||||
}
|
||||
|
||||
一些常用的Aware interface
|
||||
BeanNameAware
|
||||
ApplicationContextAware
|
||||
ResourceLoaderAware
|
||||
EnvironmentAware
|
||||
|
||||
|
||||
|
||||
## 2. BeanFactory
|
||||
|
||||
BeanFactory 提供了获取bean和判断bean的方法,包括
|
||||
|
||||
getBean(String beanName)
|
||||
getBean(String beanName, Class<T> requiredType)
|
||||
getBean(Class<T> requiredType)
|
||||
containsBean(String name)
|
||||
isSingleton(String name)
|
||||
isPrototype(String name)
|
||||
sTypeMatch(String name)
|
||||
getType(String name)
|
||||
String[] getAliases(String name);
|
||||
|
||||
## 3. BeanPostProcessor
|
||||
|
||||
## 4. BeanFactoryPostProcessor
|
||||
|
||||
## 5. ApplicationContext
|
||||
|
||||
## 6. InitializingBean
|
||||
|
||||
---
|
||||
|
||||
## 6. Springboot DefaultSingletonBeanRegistry
|
||||
|
||||
<sub>bid: `2022121513519200000000000002`</sub>
|
||||
|
||||
## DefaultSingletonBeanRegistry
|
||||
|
||||
DefaultSingletonBeanRegistry 是Springboot bean 自动装配的核心实现类。
|
||||
顾名思义,是默认的单例bean 注册器。
|
||||
|
||||
## x.1 properties
|
||||
|
||||
### dependentBeanMap
|
||||
|
||||
存放(依赖对象key -> 被依赖Set)的 ConcurrentHashMap
|
||||
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
|
||||
|
||||
### dependenciesForBeanMap
|
||||
|
||||
存放(被依赖对象key -> 依赖Set)的 ConcurrentHashMap
|
||||
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
|
||||
###
|
||||
|
||||
## x.2 methods
|
||||
|
||||
### registerDependentBean
|
||||
|
||||
我们知道Spring DI 依赖注入,此方法就是依赖注入的核心组成。注意,这段逻辑是加锁的,即存在并行处理逻辑。
|
||||
Springboot 在 AbstractApplicationContext 进行 refresh 操作时候,在最后一步会实例化bean,参照AbstractApplicationContext.finishBeanFactoryInitialization 方法,此方法的流程我们会在xxx讲解。源码如下
|
||||
|
||||
|
||||
/**
|
||||
* Register a dependent bean for the given bean。
|
||||
*/
|
||||
public void registerDependentBean(String beanName, String dependentBeanName) {
|
||||
String canonicalName = canonicalName(beanName);
|
||||
synchronized (this.dependentBeanMap) {
|
||||
Set<String> dependentBeans =
|
||||
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
|
||||
if (!dependentBeans.add(dependentBeanName)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this.dependenciesForBeanMap) {
|
||||
Set<String> dependenciesForBean =
|
||||
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
|
||||
dependenciesForBean.add(canonicalName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DI 在此处的实现逻辑可以通过下面例子来通俗说明:
|
||||
① A对象 依赖 B对象
|
||||
② A对象 依赖 C对象
|
||||
③ D对象 依赖 B对象
|
||||
所以 必须要提前先把 B 对象和 C对象进行实例化后,才能 创建A对象和 D对象。
|
||||
我们从RootBeanDefinition中获取 dependsOn的数组,遍历调用registerDependentBean方法
|
||||
参数beanName是依赖对象,即 B 或者 C,参数dependentBeanName是被依赖对象A
|
||||
1. 传入参数是 (B,A) 时,为 B 创建一个 LinkedHashSet,并在该Set中存放A, 并把 B -> Set的映射关系放入dependentBeanMap中。
|
||||
紧接着 为 A 创建一个 LinkedHashSet, 并在该Set中存放B,并把 A -> Set的映射关系放入dependenciesForBeanMap中。
|
||||
2. 传入参数是 (C, A) 时,为 C 创建一个 LinkedHashSet,并在该Set中存放A, 并把 C -> Set的映射关系放入dependentBeanMap中。
|
||||
因为 dependenciesForBeanMap 已经存在了 A -> Set这个映射,我们只需将 C 也存入这个Set就可以。
|
||||
3. 传入参数是 (B, D) 时,因为 dependentBeanMap 已经存在了 B -> Set这个映射,我们只需将 D 也存入这个Set就可以。
|
||||
紧接着 为 D 创建一个 LinkedHashSet, 并在该Set中存放B,并把 D -> Set的映射关系放入dependenciesForBeanMap中。
|
||||
|
||||
`注意` 依赖和被依赖总是成对出现的,dependentBeans.add(dependentBeanName) 返回 false,视为添加失败,即视为已经存在,根据 依赖和被依赖总是成对出现的,所以后面dependenciesForBean.add(canonicalName) 这部分就没必要执行了,因为执行了也没有意义,所以直接 return了。毕竟dependenciesForBean中已经存在了canonicalName。
|
||||
|
||||
---
|
||||
|
||||
## 7. Springboot SpringApplication
|
||||
|
||||
<sub>bid: `2022121513597200000000000003`</sub>
|
||||
|
||||
## SpringApplication
|
||||
|
||||
## x.1 properties
|
||||
|
||||
### sources
|
||||
|
||||
### environment
|
||||
|
||||
### webApplicationType
|
||||
|
||||
###
|
||||
|
||||
## x.2 methods
|
||||
|
||||
### run
|
||||
|
||||
1. 创建 DefaultBootstrapContext
|
||||
2. 获取 SpringApplicationRunListeners
|
||||
3. 转换 ApplicationArguments 将commandLine args
|
||||
4. 装备环境 ConfigurableEnvironment
|
||||
5. 配置忽略bean情报 configureIgnoreBeanInfo
|
||||
6. 创建 ApplicationContext
|
||||
7. 准备 ApplicationContext prepareContext
|
||||
设置上下文环境,加载source
|
||||
8. 刷新 ApplicationContext
|
||||
9. 刷新后处理
|
||||
|
||||
---
|
||||
|
||||
## 8. Springboot AbstractBeanFactory
|
||||
|
||||
<sub>bid: `2022121515315000000000000004`</sub>
|
||||
|
||||
## AbstractBeanFactory
|
||||
|
||||
|
||||
## x.2 methods
|
||||
|
||||
### doGetBean
|
||||
AbstractBeanFactory 的核心方法 doGetBean,doGetBean方法是由getBean调用的,如果有dependsOn存在,就会将注册依赖, 然调用getBean(dep),从而形成了递归操作。
|
||||
当不存dependsOn的时候mbd.isSingleton 时候,就会调用 getSingleton方法,子类会回调createBean(beanName, mbd, args)方法,去创建bean。
|
||||
|
||||
源码
|
||||
|
||||
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType) {
|
||||
String beanName = transformedBeanName(name);
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
checkMergedBeanDefinition(mbd, beanName, args);
|
||||
|
||||
// Guarantee initialization of beans that the current bean depends on.
|
||||
String[] dependsOn = mbd.getDependsOn();
|
||||
if (dependsOn != null) {
|
||||
for (String dep : dependsOn) {
|
||||
if (isDependent(beanName, dep)) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
|
||||
}
|
||||
registerDependentBean(dep, beanName);
|
||||
try {
|
||||
getBean(dep);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create bean instance.
|
||||
if (mbd.isSingleton()) {
|
||||
sharedInstance = getSingleton(beanName, () -> {
|
||||
try {
|
||||
return createBean(beanName, mbd, args);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
// Explicitly remove instance from singleton cache: It might have been put there
|
||||
// eagerly by the creation process, to allow for circular reference resolution.
|
||||
// Also remove any beans that received a temporary reference to the bean.
|
||||
destroySingleton(beanName);
|
||||
throw ex;
|
||||
}
|
||||
});
|
||||
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## 9. Springboot AbstractAutowireCapableBeanFactory
|
||||
|
||||
<sub>bid: `2022121523309700000000000005`</sub>
|
||||
|
||||
## AbstractAutowireCapableBeanFactory
|
||||
|
||||
父类是AbstractBeanFactory,实现了AutowireCapableBeanFactory 的方法,其中最主要的还是createBean方法。
|
||||
源码
|
||||
|
||||
/**
|
||||
* Central method of this class: creates a bean instance,
|
||||
* populates the bean instance, applies post-processors, etc.
|
||||
* @see #doCreateBean
|
||||
*/
|
||||
@Override
|
||||
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
|
||||
throws BeanCreationException {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating instance of bean '" + beanName + "'");
|
||||
}
|
||||
RootBeanDefinition mbdToUse = mbd;
|
||||
|
||||
// Make sure bean class is actually resolved at this point, and
|
||||
// clone the bean definition in case of a dynamically resolved Class
|
||||
// which cannot be stored in the shared merged bean definition.
|
||||
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
|
||||
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
|
||||
mbdToUse = new RootBeanDefinition(mbd);
|
||||
mbdToUse.setBeanClass(resolvedClass);
|
||||
}
|
||||
|
||||
// Prepare method overrides.
|
||||
try {
|
||||
mbdToUse.prepareMethodOverrides();
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
|
||||
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
|
||||
"BeanPostProcessor before instantiation of bean failed", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Finished creating instance of bean '" + beanName + "'");
|
||||
}
|
||||
return beanInstance;
|
||||
}
|
||||
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
|
||||
// A previously detected exception with proper bean creation context already,
|
||||
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(
|
||||
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## 10. Springboot AbstractBeanDefinition
|
||||
|
||||
<sub>bid: `2022121523361800000000000006`</sub>
|
||||
|
||||
## AbstractBeanDefinition
|
||||
|
||||
AbstractBeanDefinition 定义了 一个bean的各种描述。
|
||||
|
||||
## x.1
|
||||
|
||||
### String[] dependsOn
|
||||
|
||||
### Resource resource
|
||||
|
||||
###
|
||||
|
||||
---
|
||||
|
||||
## 11. Springboot ConfigurationClassPostProcessor
|
||||
|
||||
<sub>bid: `2022121612282000000000000009`</sub>
|
||||
|
||||
## ConfigurationClassPostProcessor
|
||||
|
||||
这一节主要讲的是ConfigurationClassPostProcessor和他的一些生态。
|
||||
他的生态有
|
||||
- ConfigurationClassParser 真正的处理者。
|
||||
- ComponentScanAnnotationParser 扫描classpath,把beanName加入到registry的beanDefinitionMap中。
|
||||
- doProcessConfigurationClass 是该 postProcessor最核心的处理方法
|
||||
|
||||
/**
|
||||
* Apply processing and build a complete {@link ConfigurationClass} by reading the
|
||||
* annotations, members and methods from the source class. This method can be called
|
||||
* multiple times as relevant sources are discovered.
|
||||
* @param configClass the configuration class being build
|
||||
* @param sourceClass a source class
|
||||
* @return the superclass, or {@code null} if none found or previously processed
|
||||
*/
|
||||
@Nullable
|
||||
protected final SourceClass doProcessConfigurationClass(
|
||||
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
|
||||
throws IOException {
|
||||
|
||||
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
|
||||
// Recursively process any member (nested) classes first
|
||||
processMemberClasses(configClass, sourceClass, filter);
|
||||
}
|
||||
// Process any @PropertySource annotations
|
||||
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
|
||||
sourceClass.getMetadata(), PropertySources.class,
|
||||
org.springframework.context.annotation.PropertySource.class)) {
|
||||
if (this.environment instanceof ConfigurableEnvironment) {
|
||||
processPropertySource(propertySource);
|
||||
}
|
||||
else {
|
||||
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
|
||||
"]. Reason: Environment must implement ConfigurableEnvironment");
|
||||
}
|
||||
}
|
||||
// Process any @ComponentScan annotations
|
||||
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
|
||||
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
|
||||
if (!componentScans.isEmpty() &&
|
||||
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
|
||||
for (AnnotationAttributes componentScan : componentScans) {
|
||||
// The config class is annotated with @ComponentScan -> perform the scan immediately
|
||||
Set<BeanDefinitionHolder> scannedBeanDefinitions =
|
||||
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
|
||||
// Check the set of scanned definitions for any further config classes and parse recursively if needed
|
||||
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
|
||||
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
|
||||
if (bdCand == null) {
|
||||
bdCand = holder.getBeanDefinition();
|
||||
}
|
||||
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
|
||||
parse(bdCand.getBeanClassName(), holder.getBeanName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process any @Import annotations
|
||||
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
|
||||
|
||||
// Process any @ImportResource annotations
|
||||
AnnotationAttributes importResource =
|
||||
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
|
||||
if (importResource != null) {
|
||||
String[] resources = importResource.getStringArray("locations");
|
||||
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
|
||||
for (String resource : resources) {
|
||||
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
|
||||
configClass.addImportedResource(resolvedResource, readerClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Process individual @Bean methods
|
||||
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
|
||||
for (MethodMetadata methodMetadata : beanMethods) {
|
||||
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
|
||||
}
|
||||
|
||||
// Process default methods on interfaces
|
||||
processInterfaces(configClass, sourceClass);
|
||||
|
||||
// Process superclass, if any
|
||||
if (sourceClass.getMetadata().hasSuperClass()) {
|
||||
String superclass = sourceClass.getMetadata().getSuperClassName();
|
||||
if (superclass != null && !superclass.startsWith("java") &&
|
||||
!this.knownSuperclasses.containsKey(superclass)) {
|
||||
this.knownSuperclasses.put(superclass, configClass);
|
||||
// Superclass found, return its annotation metadata and recurse
|
||||
return sourceClass.getSuperClass();
|
||||
}
|
||||
}
|
||||
|
||||
// No superclass -> processing is complete
|
||||
return null;
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## 12. Spring ASM
|
||||
|
||||
<sub>bid: `2022121615335100000000000010`</sub>
|
||||
|
||||
## asm
|
||||
|
||||
在spring中其实很少听到asm这个关键字,asm -> Assembly,跟多是指汇编语言。
|
||||
|
||||
spring也是用到asm,而且asm包是spring-core的组成部分。spring asm 大体上跟 原生的asm 都是差不多了。
|
||||
|
||||
(参照链接)[ https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/asm/package-summary.htm ]
|
||||
|
||||
|
||||
package org.springframework.asm
|
||||
Spring's repackaging of ASM 9.x (with Spring-specific patches; for internal use only).
|
||||
This repackaging technique avoids any potential conflicts with dependencies on ASM at the application level or from third-party libraries and frameworks.
|
||||
|
||||
As this repackaging happens at the class file level, sources and javadocs are not available here.
|
||||
|
||||
从package-info可以看出,repackaging,avoids any potential conflicts
|
||||
|
||||
---
|
||||
|
||||
## 13. PostProcessorRegistrationDelegate
|
||||
|
||||
<sub>bid: `2022121915527600000000000011`</sub>
|
||||
|
||||
## PostProcessorRegistrationDelegate
|
||||
|
||||
作为 Delegate 提供了 静态方法 invokeBeanFactoryPostProcessors。
|
||||
|
||||
主要是将beanFactory和beanFactoryPostProcessors做matching,
|
||||
然后对beanFactoryPostProcessors做遍历调用postProcessBeanFactory(beanFactory)
|
||||
|
||||
## ConfigurationClassPostProcessor
|
||||
|
||||
postProcessBeanFactory(beanFactory)
|
||||
|
||||
processConfigBeanDefinitions(beanFactory)
|
||||
|
||||
核心的 处理方法,
|
||||
|
||||
---
|
||||
|
||||
## 14. Spring Aop
|
||||
|
||||
<sub>bid: `2022122715594900000000000012`</sub>
|
||||
|
||||
## Aop
|
||||
Aspect Oriented Programming的缩写,意为:面向切面编程。
|
||||
Spring aop 作为spring的一个模块,在springboot中是在bean的实例化中使用的。
|
||||
|
||||
AbstractAutowireCapableBeanFactory#initializeBean
|
||||
|
||||
|
||||
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
|
||||
if (System.getSecurityManager() != null) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
invokeAwareMethods(beanName, bean);
|
||||
return null;
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
# 实现了Aware接口<BeanNameAware><BeanClassLoaderAware><BeanFactoryAware>会invoke对应的set方法。
|
||||
invokeAwareMethods(beanName, bean);
|
||||
}
|
||||
|
||||
Object wrappedBean = bean;
|
||||
if (mbd == null || !mbd.isSynthetic()) {
|
||||
# beanPostProcesser的before方法。
|
||||
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
|
||||
}
|
||||
|
||||
try {
|
||||
# 实现了InitializingBean接口的会invoke afterPropertiesSet方法。
|
||||
invokeInitMethods(beanName, wrappedBean, mbd);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(
|
||||
(mbd != null ? mbd.getResourceDescription() : null),
|
||||
beanName, "Invocation of init method failed", ex);
|
||||
}
|
||||
if (mbd == null || !mbd.isSynthetic()) {
|
||||
# beanPostProcesser的after方法。
|
||||
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
|
||||
}
|
||||
|
||||
return wrappedBean;
|
||||
}
|
||||
|
||||
AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
|
||||
// Ignore AOP infrastructure such as scoped proxies.
|
||||
return bean;
|
||||
}
|
||||
|
||||
if (bean instanceof Advised) {
|
||||
Advised advised = (Advised) bean;
|
||||
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
|
||||
// Add our local Advisor to the existing proxy's Advisor chain...
|
||||
if (this.beforeExistingAdvisors) {
|
||||
advised.addAdvisor(0, this.advisor);
|
||||
}
|
||||
else {
|
||||
advised.addAdvisor(this.advisor);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEligible(bean, beanName)) {
|
||||
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
|
||||
if (!proxyFactory.isProxyTargetClass()) {
|
||||
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
|
||||
}
|
||||
proxyFactory.addAdvisor(this.advisor);
|
||||
customizeProxyFactory(proxyFactory);
|
||||
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
}
|
||||
# ProxyFactory 其实就是创建动态代理或者Cglib的 InvocationHandler调用Proxy.newProxyInstance返回代理对象
|
||||
return proxyFactory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
// No proxy needed.
|
||||
return bean;
|
||||
}
|
||||
|
||||
---
|
||||
@@ -0,0 +1,351 @@
|
||||
# 深入理解Java虚拟机
|
||||
|
||||
> 来源:原 mongodb 集合 `t_blog` · 整理日期:2026-08-25
|
||||
> 分类 ID:`00000000000003` · 排序:0 · 文章数:9
|
||||
|
||||
## 目录
|
||||
|
||||
1. [谈一下垃圾回收算法](#谈一下垃圾回收算法)
|
||||
2. [Jvm](#jvm)
|
||||
3. [2.1运行时数据区域](#21运行时数据区域)
|
||||
4. [2.2对象内存](#22对象内存)
|
||||
5. [13.1线程安全与锁优化](#131线程安全与锁优化)
|
||||
6. [12.1Java内存模型和线程](#121java内存模型和线程)
|
||||
7. [12.2关键字volatile](#122关键字volatile)
|
||||
8. [12.3 原子性,可见性,有序性](#123-原子性可见性有序性)
|
||||
9. [12.4 Happens-Before](#124-happens-before)
|
||||
|
||||
---
|
||||
|
||||
## 1. 谈一下垃圾回收算法
|
||||
|
||||
<sub>bid: `00000000000001`</sub>
|
||||
|
||||
## Garbage Collection アルゴリズム ##
|
||||
---
|
||||
[参考文章](https://blog.csdn.net/Extraordinarylife/article/details/124309502)
|
||||
|
||||
  对于Java开发人员来说,失去了引用的对象通常会被GC回收,而我们的方法都是我们的虚拟机栈中执行的,方法执行完毕后,引用被销毁,剩余的对象自然而然就是会被GC回收。
|
||||
就如参考文章所说,JVM种虚拟机栈是随线程开启而创建,线程销毁而结束。
|
||||
  由于新的垃圾回收器G1的的出现,和方法区(永久代)的废弃,取而代之的metaspace的出现,以往的游戏知识已过时。但是其中的基本算法思想还是学要学习的。
|
||||
|
||||
基本算法思想:
|
||||
### 标记-清除(mark-sweep)
|
||||
---
|
||||
标记是一个过程,他是通过**可达性分析算法**来实现的。
|
||||
这里标记的通常是不被清除的对象,因为这样的对象很少。因为大多数对象的都是一次性的。
|
||||
清除也是一个过程,它是基于**分代收集理论**实现的。清除未被标记的对象。
|
||||
这种算法很明显会产生内存碎片化,内存不连续会直接导致提前gc的发生。
|
||||
|
||||
为了避免内存的碎片化,就有了复制算法。
|
||||
### 复制算法
|
||||
---
|
||||
复制算法是建立在标记清除算法的基础上的(即也存在标记和清除的过程)。
|
||||
标记存活对象,复制对象到存活区,清除其他区域所有对象。
|
||||
复制算法,既然是复制,就要保证左右两边的容量相等,否则会出现数组越界等情况。
|
||||
这种算法典型用在年轻代的minor gc上。
|
||||
基于这种算法,jvm内存 配分成eden,s0和s1,8:1:1的比例。
|
||||
其中s0和s1的0和1没有任何区别。这样的设计比例较好的保证了大部分对象的产生和一小部分的对象进入存活区。虽然牺牲了1的部分的空间。
|
||||
new 出来的对象都会在eden中,当eden满了后,触发minor gc,
|
||||
将存活对象移动到s0,清除eden,当eden第二次满了之后,将存活对象复制到s1,清除eden和s0,以后反复如此。
|
||||
### a
|
||||
|
||||
---
|
||||
|
||||
## 2. Jvm
|
||||
|
||||
<sub>bid: `00000000000004`</sub>
|
||||
|
||||
# Jvm #
|
||||
---
|
||||
Java Virtual Machine(Jvm)Java虚拟机
|
||||
是跨平台的关键因素。
|
||||
对于Jvm需要了解的有两大模块,一是内存模型,二是垃圾回收机制。
|
||||
## 内存模型 ##
|
||||
---
|
||||
随着Java的版本的变化,内存模型也有一定的变化。
|
||||
比如Java8后,Metaspace代替了permanent space。
|
||||
但是内存模型这个抽象概念没有消失,java虚拟机内存通常被划分为以下几个部分。
|
||||
heap:
|
||||
stack:
|
||||
native method stack
|
||||
程序计数器
|
||||
|
||||
## 垃圾回收机制/Garbage collections ##
|
||||
---
|
||||
垃圾回收机制包括算法部分和堆内存分配两部分。
|
||||
heap内存:按照java的对象的存活时间分为以下几个部分。
|
||||
Young Generation:
|
||||
Old Generation:
|
||||
由于Meta Space是使用本地内存,他不算到heap里面。
|
||||
Eden space:所有的
|
||||
|
||||
---
|
||||
|
||||
## 3. 2.1运行时数据区域
|
||||
|
||||
<sub>bid: `2022050519382600000000000012`</sub>
|
||||
|
||||
### 1.程序计数器(Program Counter Register)
|
||||
---
|
||||
**线程独有**:用于记录线程的指令位置(内存地址)。
|
||||
 如果线程正在执行的是一个java方法,那么程序计数器记录的正在执行的虚拟机字节码指令的地址;如果,正在执行的是本地Native方法,这个计数器的值应该为空(Undefined)。
|
||||
此内存区域是《Java虚拟机规范》中唯一没有规定OutofMemoryError的区域。
|
||||
|
||||
### 2.虚拟机栈(Java Virtual Machine Stack)
|
||||
---
|
||||
**线程独有**:是Java方法执行的内存模型。
|
||||
 每一个方法被执行的时候,Jvm会为该方法在该线程中创建一个栈帧(Stack Frame),用于存储 **局部变量表**,**操作数栈**,**动态链接**,**方法出口**等信息。
|
||||
 一个方法被调用,即为入栈(压栈),一个方法调用结束,即为出栈(弹栈)。不难发现栈是一个先进后出的QUEUE。
|
||||
局部变量表中存放了编译器可知的基本类型,对象引用,returnAddress类型(指向一条字节码指令的地址)。
|
||||
 这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)表示。其中64位的long和double需要两个槽。其余的数据类型只占一个Slot,局部变量表的所需的内存空间在编译期间完成分配,当进入一个方法后,方法需要在栈帧中分配的变量空间是完全确定的。这个“大小”是指的槽的数量。真正一个槽多大空间是虚拟机决定的。
|
||||
 虚拟机栈中有两个异常:一个是,StackOverFlowError,栈深度异常。另一个是OutofMemoryError,是可选的(当如果虚拟机栈可以动态扩展,但又申请不到足够内存时,发生,HotSpot虚拟机不允许,动态扩展)。
|
||||
|
||||
### 3.本地方法栈
|
||||
---
|
||||
**线程独有**:是Java native方法执行的空间区域。
|
||||
 本地方法栈和虚拟机的作用是极其相识的,只是本地方法栈只为native method服务。
|
||||
|
||||
### 4.Java 堆
|
||||
---
|
||||
**线程共享**:是Java对象和数组的存放的主要区域。
|
||||
 heap,随着java的技术的进步,所有的对象和数组都分配在堆中的z,已经不是那么绝对了。
|
||||
但是java垃圾回收器管理的区域就是堆。
|
||||
由于垃圾回收算法是基于分代类论的,但是随着java的垃圾回收技术的提升,堆的分配已经复杂化了,至少在G1垃圾回收器目前来看。
|
||||
|
||||
### 5.方法区
|
||||
---
|
||||
《Java虚拟机规范中的》的Non-heap。
|
||||
线程共享:存储已被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓冲的数据。
|
||||
其中运行时常量池作
|
||||
|
||||
### 6.直接内存
|
||||
---
|
||||
 既不是,运行时数据的一部分,也不是《虚拟机规范》中定义的区域,但这部分内存也会被频繁使用,而且可能导致OutOfMemoryError。
|
||||
JDK1.4加入了NIO类,引入了一种基于通道(channel)和缓冲区(buffer)的I/O模型,它可以使用native 方法直接分配堆外内存,然后通过Java堆中的DirectByteBuffer对象,作为这块内存的引用来进行操作,从而避免了Java堆和Native堆中的来回复制。
|
||||
|
||||
---
|
||||
|
||||
## 4. 2.2对象内存
|
||||
|
||||
<sub>bid: `2022050620338300000000000015`</sub>
|
||||
|
||||
在HotSpot虚拟机里,对象在堆中的存储布局可以划分为三个部分:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)
|
||||
|
||||
#### 对象头(Header)
|
||||
---
|
||||
对象头部包括两类信息,一类是用来存储对象自身的运行时数据,包括HashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,这里的偏向是指 offset,毕竟引用所指向的对象位置不是一个区域,而是一个点。
|
||||
这部分数据长度在32/64中的长度为,4个和8个字节,被称为Mark Word。
|
||||
比如在32位下,对象未被同步锁的情况下。
|
||||
| 存储内容 | bit | state |
|
||||
| - | - | - |
|
||||
| HashCode | 25 | |
|
||||
| 分代年龄 | 4 | |
|
||||
| 存储锁标志位 | 2 | |
|
||||
| 固定为0 | 1 | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 13.1线程安全与锁优化
|
||||
|
||||
<sub>bid: `2022050710179900000000000027`</sub>
|
||||
|
||||
## synchronized
|
||||
|
||||
既可以用作对象锁也可以用做类锁
|
||||
|
||||
## 类锁和对象锁
|
||||
|
||||
synchronized 对象锁: 每个对象都有一个monitor,而一个monitor只能被一个线程所拥有,当一个线程a进入monitorenter,monitor的进入数为1,同时,其他的线程就无法获取monitor,如果a再次进入monitorenter,monitor的进入数为2,所以synchronized 是可重入锁。当a monitorexit退出对象锁,则monitor的进入数减少为1,只有monitor的进入数为0时候,即没有任何线程拥有monitor
|
||||
|
||||
对象锁
|
||||
synchronizded method // 直接获取该方法的对象的对象锁
|
||||
synchronizded(object) { // 获取object的对象锁
|
||||
}
|
||||
|
||||
类锁
|
||||
static synchronizded method
|
||||
synchronizded(xxx.class) {
|
||||
}
|
||||
|
||||
-- 类锁和对象锁不会产生竞争,二者的加锁方法不会相互影响
|
||||
|
||||
## 锁在哪? biased lock 偏向锁
|
||||
|
||||
我们知道java对象有三部分组成
|
||||
对象头部 object header
|
||||
实例数据 object data
|
||||
对齐填充 向2的*n填充
|
||||
|
||||
而对象头部由Mark Word和klass pointer组成
|
||||
前8个字节为Mark Word,后4个字节为klass pointer
|
||||
其中Mark Word长度为jvm一个word的大小。64位jvm是64bit,即8个字节。
|
||||
-------------------------------------------------
|
||||
OFFSET SIZE TYPE DESCRIPTION
|
||||
0 4 (object header) markword
|
||||
0 4 (object header) markword
|
||||
8 4 (object header) klass pointer
|
||||
-------------------------------------------------
|
||||
[open jdk document](http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/markOop.hpp)
|
||||
|
||||
// 64 bits:
|
||||
// --------
|
||||
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
|
||||
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
|
||||
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
|
||||
// size:64 ----------------------------------------------------->| (CMS free block)
|
||||
//
|
||||
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
|
||||
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
|
||||
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
|
||||
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
|
||||
|
||||
unused hash unused age biased_lock lock
|
||||
25 -> 31 -> 1 -> 4 -> 1 -> 2 normal object(sum = 64)
|
||||
|
||||
|
||||
无锁状态下的mark word
|
||||
Java 对象刚创建时,还没有任何线程来竞争
|
||||
unused hash unused age biased_lock lock
|
||||
25 -> 31 -> 1 -> 4 -> 0 -> 01
|
||||
|
||||
偏向状态下的mark word
|
||||
偏向锁是指一段同步代码一直被一同个线程所访问,那么该线程会自动获取锁,降低获取锁的代价
|
||||
偏向锁在竞争不激烈的情况下,效率非常高。
|
||||
线程ID epoch unused age biased_lock lock
|
||||
54 -> 2 -> 1 -> 4 -> 1 -> 01
|
||||
|
||||
轻量级锁状态下的mark word
|
||||
当有两个线程开始竞争这个锁对象,锁会升级为轻量级锁,两个线程公平竞争,哪个线程先占有锁对象,Mark Word 就指向哪个线程的栈帧中的锁记录
|
||||
锁记录指针 lock
|
||||
62 -> 00
|
||||
|
||||
重量级锁状态的mark word
|
||||
重量级锁会让其他申请的线程之间进入阻塞,性能降低。
|
||||
重量级锁也就叫做同步锁,这个锁 对象 Mark Word 再次发生变化,会指向一个监视器(Monitor)对象,该监视器对象用集合的形 式,来登记和管理排队的线程。
|
||||
monitor对象指针 lock
|
||||
62 -> 10
|
||||
|
||||
## synchronized 的锁优化
|
||||
|
||||
JDK1.6开始的减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。
|
||||
需要注意的是锁可以升级但不能降级
|
||||
|
||||
## 关于 Object 和 Thread
|
||||
|
||||
Object 类 有native 的 wait方法
|
||||
调用wait 前, obtain monitor,
|
||||
调用wait 后,释放对象锁,然后当前线程进入等待状态wait set 队列中
|
||||
|
||||
sleep() 不释放锁,sleep 可以在没有锁的情况下调用,
|
||||
|
||||
---
|
||||
|
||||
## 6. 12.1Java内存模型和线程
|
||||
|
||||
<sub>bid: `2022050920343900000000000005`</sub>
|
||||
|
||||
### 概述
|
||||
---
|
||||
衡量一个服务性能好坏的,每秒事务处理数(Transactions Per second,TPS ),是重要指标之一。
|
||||
### 硬件的效率和一致性
|
||||
---
|
||||
 由于计算机的存储效率和计算效率的相差甚大,计算机不得不引入读写速度接近于处理器运算速度的高速缓存(cache),来作为内存和cpu直接的缓冲,但也增加了计算机系统的复杂性,尤其在多核处理器时代,每个处理器都有自己的缓存,而他们又共享同一块内存,这样就导致了一个新的问题:内存一致性。
|
||||
 为了解决内存一致性,各个处理器访问缓存时都遵循一些协议,比如MSI,MESI等,而java虚拟机同样有自己的内存模型,并且和上述介绍的硬件和缓存访问操作具有高度的类比性。
|
||||
 除了增加高速缓存之外,为了使处理器的运算单元尽量的被充分利用,处理器可能对输入的代码进行乱序执行优化,,处理器会在计算后将乱序执行的结果重组,保证最终结果一致性。
|
||||
而与处理器的乱序执行优化类似,Java虚拟机的即时编译器中也有指令重排序(Instruction Recorder)优化。
|
||||
### Java 内存模型
|
||||
---
|
||||
主内存和工作内存。
|
||||
首先要明确一点,很容易混淆的是,虚拟机栈是一个jvm运行时的存储结构,并不是运算。
|
||||
从更基础的层次来说,主内存直接对应物理硬件的内存,工作内存对应于寄存器和高速缓存。
|
||||
而工作内存直接存储了被该线程使用的变量的主内存副本,这里的副本,有可能是对象的引用或者,对象的某个字段,但不会有虚拟机把整个java对象复制一次的。
|
||||
### 内存间的交互操作
|
||||
---
|
||||
Java内存模型规定了一些操作来实现内存间交互,JVM会保证它们是原子的
|
||||
lock:
|
||||
锁定,把变量标识为线程独占,作用于主内存变量
|
||||
unlock:
|
||||
解锁,把锁定的变量释放,别的线程才能使用,作用于主内存变量
|
||||
read:
|
||||
读取,把变量从主内存读取到工作内存
|
||||
load:
|
||||
载入,把read读取到的值放入工作内存的变量副本中
|
||||
use:
|
||||
使用,把工作内存中的一个变量的值传递给执行引擎
|
||||
assign:
|
||||
复制,把从执行引擎接收到的值赋给工作内存里面的变量
|
||||
store:
|
||||
存储,把工作内存中一个变量的值传递到主内存中
|
||||
write:
|
||||
写入,把store进来的数据存放到主内存的变量中
|
||||
|
||||
注意,Java内存模型,只要求上述两个操作必须按顺序执行,但没有要求连续执行。也就是说read和load之间store与write之间是可以插入其他的指令的。比如对主内存中a,b变量进行访问时,可能出现,read a,read b,load b,load a。
|
||||
后来java设计团队,对java内存模型进行了简化,read,write,lock,unlock四种
|
||||
|
||||
---
|
||||
|
||||
## 7. 12.2关键字volatile
|
||||
|
||||
<sub>bid: `2022050921201400000000000006`</sub>
|
||||
|
||||
关键字volatile是java虚拟机提供的最轻量级的同步机制。
|
||||
当一个变量被定义为volatile时,它具备两种特性。
|
||||
第一种是**可见性**。
|
||||
即每次get时,强制刷新。
|
||||
一个误解区,字节码就是原子操作,这是不对的,一个字节码指令在解释执行时,如果是编译执行,一条字节码会有可能被转化为若干个本地机器指令。此处,用-XX:+PrintAssembly参数输出反汇编来分析。
|
||||
|
||||
第二种是,**有序性**。
|
||||
即,禁止指令重排序(Instruction Recorder)优化。
|
||||
被volatile修饰的变量,赋值后会多执行一个"lock addl"操作,这个操作相当于一个memory barrier(内存屏障),指令重拍时,不能把后面的指令排序到内存屏障前面去,只有一个处理器访问时,不需要内存屏障。
|
||||
addl 是一个空操作,(把ESP寄存器的值 + 0),这个操作相当于把缓存中的变量做了一次store和write操作。
|
||||
lock iddl指令把修改同步到内存时,意味着所有的之前的操作都已完成,便形成了指令重排序无法穿越内存屏障的效果。
|
||||
针对于long和double的类型变量,不通虚拟机是有着不同的实现,此处不做描述。
|
||||
|
||||
---
|
||||
|
||||
## 8. 12.3 原子性,可见性,有序性
|
||||
|
||||
<sub>bid: `2022050921544800000000000007`</sub>
|
||||
|
||||
Java内存模型,是围绕着在并发过程中如何处理原子性,可见性,和有序性的这三个特点建立的。
|
||||
### 原子性(Atomicity)
|
||||
---
|
||||
由Java内存模型直接保证的原子性操作有,read,load,assign,use,store,write这6个,我们大致可以认为,对基本类型的访问读写都是原子性的(long double类外)。
|
||||
尽管,虚拟机没有把lock和unlock操作直接开放给用户,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐式地这两个操作,反映到同步块就是synchronized关键字,因此synchronzed块之间的操作也属于原子操作。
|
||||
### 可见性 (Visibility)
|
||||
---
|
||||
可见性,即变量的修改对其他线程是可见的,无论是普通变量还是volatile变量都是会将结果刷新到主内存的,只是volatile是其他线程获取该变量时强制刷新到主内存,而普通变量并没有。
|
||||
除此之外,java还有两个关键字能够实现可见性,即synchronized和final
|
||||
synchronized的可见性是因为"对一个变量unlock操作之前,必须先把此变量同步回到主内存中(即执行了store,write操作)"的这条规则获得的。
|
||||
而final的可见性是指,被final修饰的字段在构造器中一旦完成了初始化,并且构造器并没有把这个引用传递出去,那么其他线程就能看见final字段。
|
||||
### 有序性 (Ordering)
|
||||
---
|
||||
Java语言提供了volatile和synchronized两个关键字来保证线程之间的操作是有序性的。
|
||||
volatile本身就是禁止了指令重排序。
|
||||
而synchronized是由"一个变量在同一时刻只允许有一条线程对其进行lock操作"
|
||||
|
||||
synchronzed能保证以上三个特性,但是并发场景会影响性能。
|
||||
|
||||
---
|
||||
|
||||
## 9. 12.4 Happens-Before
|
||||
|
||||
<sub>bid: `2022050922310000000000000008`</sub>
|
||||
|
||||
### 先行发生原则(Happens-Before)
|
||||
Java内存模型中如果所有的有序性都仅靠volatile和synchronized来完成的话,会有很多操作变得啰嗦。
|
||||
Java语言有一个先行发生原则(Happens-Before)。
|
||||
规则:
|
||||
1.程序次序规则
|
||||
2.Monitor Lock Rule
|
||||
3.volatile Variable Rule
|
||||
4.Thread Start Rule,start方法最先执行
|
||||
5.Thread Termination Rule,所有方法都优先于
|
||||
6.线程中断规则。
|
||||
7.对象终结规则,一个对象初始化完成先行发生于finalize()方法。
|
||||
8.传递性,A》B,B》C,那么A》C
|
||||
jintian
|
||||
1111
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user