ZzRG的小站 - JAVA
type: channel
link: https://www.juziw.cn/category/java/
items: gRPC的学习
items: 分布式锁Mysql篇
items: 微服务网关Gateway使用
items: Spring Security 的两种资源放行的策略
items: string常用方法
items: synchronized锁升级
items: JUC高并发volatile作用
items: 关于锁的一些知识
items: MybatisPlus的坑（ 自动驼峰命名）
items: MybatisPlus快速入门

gRPC的学习
type: item
date: 2023-09-26T13:57:33+00:00
description: 4. gRPC4.1 gRPC介绍1. gRPC是由google开源的一个高性能的RPC框架。Stubby Google内部的RPC,演化而来的，2015正式开源。云原生时代是一个RPC标准。 2. gRPC核心的设计思路 1．网络通信 --->gRPC自己封装网络通信的部分提供多种语言的网络通信的封装(C Java[Netty] Go) 2．协议---> HTTP2传输数据的时候二进制数据内容。支持双向流(双工）连接的多路复用。 3．序列化--->基本文本JSON基于二进制Java原生序列化方式 Thrift二进制的序列化压缩二级制序列化。 protobuf (Protocol Buffers) google开源一种序列化方式︰时间效率和空间效率是JSON的3---5倍。 IDL语言 4．代理的创建--->让调用者像调用本地方法那样去调用远端的服务方法。 stub 3. gRPC 与ThriftRPC区别 共性:支持异构语言的RPC。 区别: 1．网络通信 Thrift TCP 专属协议 GRPC HTTP2 2．性能角度ThriftRPC 性能高于gRPC 3. gRPC 大厂背书(Google)，云原生时代与其他组件合作的顺利。所以gRPC应用更广泛。 4. gRPC的好处 1．高效的进行进程间通信。 2．支持多种语言原生支持C Go Java实现。C语言版本上扩展C++C# NodeJs Python Ruby PHP.. 3．支持多平台运行 Linux Android IOS Mac0s windows。 4. gPRC序列化方式采用protobuf，效率高。 5．使用Http2协议 6．大厂的背书 4.2 Http2.0协议1. 回顾 Http1.x协议 Http1.0协议 请求响应的模式 短链接协议（无状态协议） 传输数据文本结构 单工 无法实现服务的推送 变相实现推送（客户端轮询的方式） Http1.1协议 请求响应的模式 有限的长连接 升级的方式WebSocket 双工 实现服务器向客户端的推送。 管道网络传输(wei'bei's) 总结Http1.x协议 共性 1. 传输数据文本格式 可读性好 但是效率差。 2. 本质上Http1.x协议无法实现双工通信。 3. 资源的请求。需要发送多次请求，建立多个连接才可以完成。 2. HTTP2.0协议 1. http2.0协议是一个二进制协议，效率高于Http1.x协议，可读性差。 2. 可以实现双工通信 3. 一个请求，一个链接 可以请求多个数据。【多路复用】（如下图） 3. Http2.0协议的三个概念 1. 数据liu stream 2. 消息 message 3. 帧 frame 4. 其他的相关概念 1. 数据流的优先级，可以通过为不同的stream设置权重，来限制不同流的传输顺序。 2. 流控 --client发送的数据太快了，server处理不过来，通知client暂停数据的发送。4.3 Protocol buffers[protobuf]1. protobuf 是一种与变成语言无关【IDL】，与集体的平台无关【OS】。 它定义的中间语言，可以方便的在clinet 与server中进行RPC的数据传输。 2. protobuf 两个版本 proto2 proto3，但是目前主流应用都是proto3. 3. protobuf 主要安装protobuf的编译器，编译器的目的，可以把protobf的IDL语言，转换成具体的某一种语言。4.3.1 protobuf的编译器安装IDE 集成4.3.2 protobuf的语法文件格式xxx.proto UserServce.proto ORderService.proto版本设定syntax = "proto3"； (IDL语言)注释1. 单行注释 // 2. 多行注释 /* */与Java语言相关的语法optin 关键字开头的 可以选择使用语法 # 后续protobuf生成的java代码 一个源文件还是多个源文件 xx.java optin java_multiple_files = false; (true为多个或者false一个) 一般情况为false # 指定protobuf生成的类 放置在哪个包中 optin jva_package= "com.suns"; # 指定protobuf生成的外部类的名字（管理内部类【内部类才是正真开发使用】） optin java_outer_classname = "UserName"; (外部类)逻辑包（了解）# 对于protobuf对于文件内容管理 package xxx；导入UserService.proto OrderService.proto import "x…

分布式锁Mysql篇
type: item
date: 2022-12-26T16:15:00+00:00
description: 分布式锁CREATE TABLE `stock` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `product_code` varchar(20) NOT NULL, `warehouse` varchar(20) NOT NULL, `count` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `idx_pc` (`product_code`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;1.JVM本地锁2.一个sql3.悲观锁：select ...from updatemysql悲观锁中使用行级锁：1.锁的查询或者更新条件必须是索引字段 2.查询或者更新条件必须是具体值 不能使用 like这样的模糊查询Mapper代码实现：@select("select * from stock where product_code = #{productCode} for update;") List<Stock> queryStock(String productCode);service实现：解决了锁定的范围 同时一个商品的多条库存记录 记录了库存变化前后的状态缺点问题： 1.性能问题 性能较低2.会产生死锁： 对多条数据加锁时，加锁的顺序要一致 3.库存操作要统一：用select ...for update 而用普通select锁不住4.mysql的乐观锁：时间戳version版本号CAS机制CREATE TABLE `stock` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `product_code` varchar(20) NOT NULL, `warehouse` varchar(20) NOT NULL, `count` int(11) NOT NULL, `version` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `idx_pc` (`product_code`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;如果version与mysql中的不相等就更新失败 再次进行尝试！servic实现：会出现栈溢出 还有超时问题！解决栈溢出问题MDL 更新删除操作 会进行加锁 就会出现超时问题去掉手动事务@Transactional（事务也会加锁） 后面当进行到 if（this....update...）时候 本来有悲观锁 当执行失败的就会放掉锁 后面就不会阻塞 最终：乐观锁存在的问题：1.高并发的情况下，性能极低。 （乐观锁适合读多写少的情况）2.CAS会产生ABA问题。3.读写分离的情况下导致乐观锁不可靠myqsl锁总结：性能：一个sql>悲观锁>JVM锁>乐观锁如果追求极致的性能，业务场景简单并不需要记录前后变化的情况下 优先选择：一个sql；如果写并发量低（读多），争抢不是很激烈的情况下 优先选择：乐观锁；如果写并发量较高，一般会经常冲突，此时选择乐观锁的话，会导致业务代码间的不断重试。 应该优先选择：悲观锁；不推荐使用JVM本地锁；

微服务网关Gateway使用
type: item
date: 2022-11-21T07:00:00+00:00
description: 微服务网关Gateway使用为什么需要网关？Gateway网关是我们服务的守门神，所有微服务的统一入口。网关的核心功能特性1. 请求路由和负载均衡 一切请求都必须先经过gateway，但网关不处理业务，而是根据某种规则，把请求转发到某个微服务，这个过程叫做路由。当然路由的目标服务有多个时，还需要做负载均衡。2. 权限控制 网关作为微服务入口，需要校验用户是是否有请求资格，如果没有则进行拦截。3. 限流 请求流量过高时，在网关中按照下流的微服务能够接受的速度来放行请求，避免服务压力过大。架构图gateway快速入门基本步骤如下：1、创建SpringBoot工程gateway，引入网关依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>2、编写启动类package com.cn.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }3、编写基础配置和路由规则server: port: 10010 # 网关端口 spring: application: name: gateway # 服务名称 cloud: nacos: server-addr: localhost:8848 # nacos地址 gateway: routes: # 网关路由配置 - id: user-service # 路由id，自定义，只要唯一即可 # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址 lb就是负载均衡，后面跟服务名称 predicates: # 路由断言，也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配，只要以/user/开头就符合要求 我们将符合Path 规则的一切请求，都代理到 uri参数指定的地址。本例中，我们将 /user/** 开头的请求，代理到lb://userservice，lb是负载均衡，根据服务名拉取服务列表，实现负载均衡。4、启动网关服务进行测试重启网关，访问 http://localhost:10010/user/1 时，符合/user/**规则，请求转发到uri：http://userservice/user/15、网关路由的流程图总结网关搭建步骤创建项目，引入nacos服务发现和gateway依赖配置application.yml，包括服务基本信息、nacos地址、路由路由配置包括路由id：路由的唯一标识路由目标（uri）：路由的目标地址，http代表固定地址，lb代表根据服务名负载均衡路由断言（predicates）：判断路由的规则，路由过滤器（filters）：对请求或响应做处理路由断言和路由过滤器断言工厂名称说明示例After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]Before是某个时间点之前的请求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]Between是某两个时间点之前的请求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]Cookie请求必须包含某些cookie- Cookie=chocolate, ch.pHeader请求必须包含某些header- Header=X-Request-Id,…

Spring Security 的两种资源放行的策略
type: item
date: 2022-11-03T14:00:00+00:00
description: 在Spring Security 中有两种资源放行的策略若是你但愿用户不用登陆就能访问，那么通常来讲，有两种配置策略：java第一种就是在 configure(WebSecurity web) 方法中配置放行，像下面这样：web@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico", "/verifyCode"); }第二种方式是在 configure(HttpSecurity http) 方法中进行配置：springhttp.authorizeRequests() .antMatchers("/hello").permitAll() .anyRequest().authenticated()两种方式最大的区别在于，第一种方式是不走 Spring Security 过滤器链，而第二种方式走 Spring Security 过滤器链，在过滤器链中，给请求放行。有的资源可使用第一种方式额外放行，不须要验证，例如前端页面的静态资源，就能够按照第一种方式配置放行。有的资源放行，则必须使用第二种方式，例如登陆接口。你们知道，登陆接口也是必需要暴露出来的，不须要登陆就能访问到的，可是咱们却不能将登陆接口用第一种方式暴露出来，登陆请求必需要走 Spring Security 过滤器链，由于在这个过程当中，还有其余事情要作。

string常用方法
type: item
date: 2022-09-12T07:57:00+00:00
description: 一、string常用方法valueOf(基础数据类型 b) 将基础类型数据的文本转换为字符串 substring(int beginIndex) 切割原字符串，返回一个新的字符串， beginIndex：表示从下标为几的地方开始切割 replace(char oldChar, char newChar) 返回一个新的字符串，它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 trim() 返回字符串的副本，忽略前导空白和尾部空白。 isEmpty() 当且仅当 length() 为 0 时返回 true。 toLowerCase() 将此 String 中的所有字符都转换为小写。 toUpperCase() 将此 String 中的所有字符都转换为大写。 二、在字符串中查询指定子字符串的位置charAt(int index) 获取指定索引处的 char 值。没有则为null indexOf(string) 返回指定字符在此字符串中第一次出现处的索引。没有则为0 lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。 lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引，从指定的索引处开始进行反向搜索。 contains(CharSequence s) 判断字符串是否包含指定的 char 值，有则返回 true。三、比较两个字符串的内容equals(Object anObject) 将此字符串与指定的对象比较。 equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较，不考虑大小写。四、判断是否以指定的字符开头或者结尾endsWith(String suffix) 判断此字符串是否以指定的后缀结束。 startsWith(String prefix) 判断此字符串是否以指定的前缀开始。五、将字符串转换为字符数组，和字节数组format(Locale l, String format, Object... args) 格式字符串和参数返回一个格式化字符串。 getBytes(String charset) 将string转换为byte 数组。 getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将string转换为字符数组 toCharArray() 将此字符串转换为一个新的字符数组。六、正则表达式matches(String regex) 判断此字符串是否匹配给定的正则表达式。 replaceAll(String regex, String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 split(String regex) 根据给定正则表达式的匹配拆分此字符串。七、StringBuffer/StringBuilder类常用方法StringBuffer 上的主要操作是 append 和 insert 方法， append 方法始终将这些字符添加到缓冲区的末端；而 insert 方法则在指定的索引下添加字符。 StringBuffer(String str) 构造一个字符串缓冲区，并将其内容初始化为指定的字符串内容。 append(基础类型数据 b) 将 基础类型的字符串表示形式追加到序列后面。 charAt(int index) 返回此序列中指定索引处的 char 值。 delete(int start, int end) 移除此序列的子字符串中的字符。 deleteCharAt(int index) 移除此序列指定位置的 char。 insert(int offset, 基础类型数据 b) 将 基础类型参数的字符串表示形式插入指定位置中。 toString() 返回此序列中数据的字符串表示形式。

synchronized锁升级
type: item
date: 2022-07-27T16:03:00+00:00
description: synchronized的变化①. java5以前,只有Synchronized,这个是操作系统级别的重量级操作,重量级锁,假如锁的竞争比较激烈的话,性能下降②. 在Java早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”,时间成本相对较高,这也是为什么早期的synchronized效率低的原因。Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁③. 为什么每一个对象都可以成为一个锁?Java对象是天生的Monitor,每一个Java对象都有成为Monitor的潜质,因为在Java的设计中 ,每一个Java对象自打娘胎里出来就带了一把看不见的锁,它叫做内部锁或者Monitor锁。Monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的转换,成本非常高④. Mutex LockMonitor是在jvm底层实现的,底层代码是c++。本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的转换,状态转换需要耗费很多的处理器时间成本非常高。所以synchronized是Java语言中的一个重量级操作。⑤. Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁,需要有个逐步升级的过程,别一开始就捅到重量级锁⑥. synchronized锁:由对象头中的Mark Word根据锁标志位的不同而被复用及锁升级策略无锁偏向锁单个线程多次访问定义：①. 主要作用:当一段同步代码一直被同一个线程多次访问,由于只有一个线程那么该线程在后续访问时便会自动获得锁(偏向锁)同一个老顾客来访,直接老规矩行方便偏向锁为了解决只有在一个线程执行同步时提高性能②. 64位标记图再看(通过CAS方式修改markword中的线程ID)③. 偏向锁的理论在实际应用运行过程中发现,“锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用他的线程拥有,这个线程就是锁的偏向线程那么只需要在锁第一次被拥有的时候,记录下偏向线程ID。这样偏向线程就一直持有着锁(后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接比较对象头里面是否存储了指向当前线程的偏向锁)。如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了,直到竞争发生才释放锁。以后每次同步,检查锁的偏向线程ID与当前线程ID是否一致,如果一致直接进入同步。无需每次加锁解锁都去CAS更新对象头。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。假如不一致意味着发生了竞争,锁已经不是总是偏向于同一个线程了,这时候可能需要升级变为轻量级锁,才能保证线程间公平竞争锁。偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的④. 技术实现一个synchronized方法被一个线程抢到了锁时,那这个方法所在的对象就会在其所在的Mark Word中将偏向锁修改状态位,同时还会有占用前54位来存储线程指针作为标识。若该线程再次访问同一个synchronized方法时,该线程只需去对象头的Mark Word 中去判断一下是否有偏向锁指向本身的ID,无需再进入Monitor去竞争对象了。⑤. 对于如上的③、④进行细化锁对比偏向锁的操作不用直接捅到操作系统，不涉及用户到内核转换，不必要直接升级为最高级，我们以一个account对象的“对象头”为例，假如有一个线程执行到synchronized代码块的时候，JVMM使用CAS操作把线程指针ID记录到Mark Word当中，并修改标偏向标示，标示当前线程就获得该锁。锁对象变成偏向锁（通过CAS修改对象头里的锁标志位〉，字面意思是“偏向于第一个获得它的线程”的锁。执行完同步代码块后，线程并不会主动释放偏向锁。这时线程获得了锁，可以执行同步代码块。当该线程第二次到达同步代码块时会判断此时持有锁的线程是否还是自己（持有锁的线程ID也在对象头里)，JVM通过account对象的Mark Word判断:当前线程ID还在，说明还持有着这个对象的锁，就可以继续进入临界区工作。由于之前没有释放锁，这里也就不需要重新加锁。如果自始至终使用锁的线程风有一个，很明显偏向锁几乎没有额外开销，性能极高。结论:JVM不用和操作系统协商设置Mutex(争取内核)，它…

JUC高并发volatile作用
type: item
date: 2022-07-26T15:35:07+00:00
description: volatile的特性可见性:可见性：保证不同线程对某个变量完成操作后结果及时可见，即该共享变量一旦改变所有线程立即可见诉求:1.线程中修改了自己工作内存中的副本之后，立即将其刷新到主内存;2.工作内存中每次读取共享变量时，都去主内存中重新读取，然后拷贝到工作内存。使用volatile修饰共享变量，就可以达到上面的效果，被volatile修改的变量有以下特点:1.线程中读取的时候，每次读取都会去主内存中读取共享变量最新的值，然后将其复制到工作内存2.线程中修改了工作内存中变量的副本，修改之后会立即刷新到主内存没有原子性：volatile变量不适合参与到依赖当前值的运算，如i =i+ 1; i++;之类的那么依靠可见性的特点volatile可以用在哪些地方呢?通常volatile用做保存某个状态的boolean值or int值。由于volatile变量只能保证可见性，在不符合以下两条规则的运算场景中，我们仍然要通过加锁(使用synchronized、java.util.concurrent中的锁或原子类）来保证原子性·运算结果并不依赖变量的当前值，或者能够确保只有单一的线程修改变量的值.·变量不需要与其他的状态变量共同参与不变约束。禁止指令重排重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段，有时候会改变程序语句的先后顺序不存在数据依赖关系，可以重排序;存在数据依赖关系，禁止重排序四种内存屏障策略内存屏障︰是一种屏障指令，它使得CPU或编译器对屏障指令的前和后所发出的内存操作执行一个排序的约束。也叫内存栅栏或栅栏指令作用：阻止屏障两边的指令重排序写数据时加入屏障，强制将线程私有工作内存的数据刷回主物理内存读数据时加入屏障，线程私有工作内存的数据失效，重新到主物理内存中获取最新的数据happens-before之volatile变量规则①.当第一个操作为volatile读时,不论第二个操作是什么,都不能重排序。这个操作保证了volatile读之后的操作不会被重排到volatile读之前②.当第二个操作为volatile写时,不论第一个操作是什么,都不能重排序。这个操作保证了volatile写之前的操作不会被重排到volatile写之后③.当第一个操作为volatile写时,第二个操作为volatile读时,不能重排①. 写在每个volatile写操作的前⾯插⼊⼀个StoreStore屏障在每个volatile写操作的后⾯插⼊⼀个StoreLoad屏障写指令：②. 读在每个volatile读操作的后⾯插⼊⼀个LoadLoad屏障在每个volatile读操作的后⾯插⼊⼀个LoadStore屏障读指令：数据依赖性:若两个操作访问同一变量，且这两个操作中有一个为写操作，此时两操作间就存在数据依赖性。volatile的用法状态标志，判断业务是否结束使用:作为一个布尔状态标志，用于指示发生了一个重要的一次性事件，例如完成初始化或任务结 理由:状态标志并不依赖于程序内任何其他状态，且通常只有一种状态转换例子:判断业务是否结京public class UseVolatileDemo{ private volatile static boolean flag = true; public static void main(String[] args){ new Thread(() -> { while(flag) { //do something...... } },"t1").start(); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(2L); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { flag = false; },"t2").start(); } } 开销较低的读,写锁策略public class UseVolatileDemo{ /** * 使用:当读远多于写，结合使用内部锁和 volatile 变量来减少同步的开销 * 理由:利用volatile保证读取操作的可见性;利用synchronized保证复合操作的原子性 */ public class Counter{ private volatile int value; public int getValue(){ return value; //利用volatile保证读取操作的可见性 } public synchronized int increment(){ return value++; //利用synchronized保证复合操作的原子性 } } } 单列模式 DCL双端锁的发布public class Sa…

关于锁的一些知识
type: item
date: 2022-07-25T12:44:00+00:00
description: 乐观锁和悲观锁1、 悲观锁(synchronized关键字和Lock的实现类都是悲观锁)什么是悲观锁？认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改适合写操作多的场景,先加锁可以保证写操作时数据正确(写操作包括增删改)、显式的锁定之后再操作同步资源synchronized关键字和Lock的实现类都是悲观锁2、 乐观锁概念:乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作乐观锁在Java中通过使用无锁编程来实现，最常采用的时CAS算法,Java原子类中的递增操作就通过CAS自旋实现的适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅度提升乐观锁一般有两种实现方式(采用版本号机制、CAS算法实现) //悲观锁的调用方式 public synchronized void m1(){ //加锁后的业务逻辑 } //保证多个线程使用的是同一个lock对象的前提下 ReetrantLock lock=new ReentrantLock(); public void m2(){ lock.lock(); try{ //操作同步资源 }finally{ lock.unlock(); } } //乐观锁的调用方式 //保证多个线程使用的是同一个AtomicInteger private AtomicInteger atomicIntege=new AtomicInteger(); atomicIntege.incrementAndGet();对象锁/类锁【强制】高并发时，同步调用应该去考量锁的性能损耗。能用无锁数据结构，就不要用锁;能锁区块，就不要锁整个方法体﹔能用对象锁，就不要用类锁。说明︰尽可能使加锁的代码块工作量尽可能的小，避免在锁代码块中调用RPC方法。作用于实例方法，当前实例加锁，进入同步代码前要获得当前实例的锁;作用于代码块，对括号里配置的对象加锁。作用于静态方法，当前类加锁，进去同步代码前要获得当前类对象的锁;//入口 public static void main(string[] args){ Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { phone. sendEmail(); }, name: "a" ).start(); //暂停毫秒,保证a线程先启动 try { TimeUnit.ILLISECONDs.sleep( timeout 200); } catch (InterruptedException e) { e.printstackTrace(); } new Thread(() -> { phone.sendSMS( ); // phone.hello(); // phone2.sendSMS(); }, name: "b" ).start(); } class Phone{ public synchronized void sendEmail(){ try { TimeUnit.SECONDs.sleep( timeout: 3); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println( "-----sendEmail" ); } public synchronized void sendsMs(){ system.out.println( "-----sendSMS" ); } public void hello(){ system.out.println( "-----hello" ); } }*一个对象里面如果有多个synchronized方法，某一个时刻内，只要一个线程去调归其中的一个synchronized方法了，*其它的线程都只能等待，换句话说，某一个时刻内，只能有唯一的一个线程去访问这些synchronized方法*锁的是当前对象this，被锁定后，其它的线程都不能进入到当前对象的其它的synchronized方法//入口 public static void main(string[] args){ Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { phone. sendEmail(); }, name: "a" ).start(); //暂…

MybatisPlus的坑（ 自动驼峰命名）
type: item
date: 2022-06-28T15:29:54+00:00
description: MybatisPlus 自动驼峰命名在使用 'mybatis-plus '时,驼峰命名映射是自动开启的，即：/* 实体类中的字段 userName 自动映射对应数据库中的 user_name, 如果数据库表中字段不是 user_name 字段，而是 userName 此时Java程序就会报错 */ private String userName;此时就需要关闭自动驼峰命名映射：spring: <settings> <setting name="mapUnderscoreToCamelCase" value="false"/> </settings>springboot:#在springboot的yml配置文件中配置 mybatis-plus: configuration: map-underscore-to-camel-case: false自动生成器的使用MyBatisX插件 自动根据数据生成domian 实体对象 Mapper对象（操作数据库的对象）、Mapper.xml（定义了Mapper对象和数据库的关联，可以在里面写自己的SQL）、Service（常见的增删改查）、Service的实现类

MybatisPlus快速入门
type: item
date: 2022-06-27T12:24:00+00:00
description: MybatisPlus快速入门使用第三方组件：1、导入对应的依赖2、研究依赖是如何配置3、代码的编写4、提高扩展技术能力！步骤创建数据库mybatis_plus创建User表DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com'); 真是开发中，version（乐观锁）、deleted（逻辑删除）、gmt_create、gmt_modified编写项目。初始化项目！使用SpringBoot初始化导入依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency>使用MybatisPlus可以节省大量代码，尽量不要同时导入mybatis和mybatisplus 版本差异！、连接数据库 这一步和Mybatis相同！#mysql 5驱动不同 com.mysql.cj.jdbc.Driver spring.datasource.username=root spring.datasource.password=juzi spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?userSSL=false&userUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #mysql 8 驱动不同com.mysql.cj.jdbc.Driver 需要增加时区配置 serverTimezone=GMT%2B8pojo -dao(之前需要 连接mybatis 配置mapper.xml)-service-controller使用了Mybatis-Plus 之后pojo/** * @author: ZzRG * @version: 1.0 * Date: 2022/6/27 */ @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }mapper接口/** * @author: ZzRG * @version: 1.0 * Date: 2022/6/27 */ //在对应的Mapper上面继承基本的接口 BaseMapper public interface UserMapper extends BaseMapper<User> { // 所有的curd 操作编写完成了 // 不需要像以前那样写一大堆文件 }注意点：我们需要在主启动器上去扫描 我们的Mapper包下的所有接口 @MapperScan("com.zzrg.xuexi.mapper")测试类中测试@Spri…