线程安全 - 如果线程执行过程中不会产生共享资源的冲突,则线程安全。
线程不安全 - 如果有多个线程同时在操作主内存中的变量,则线程不安全
实现线程安全的三种方式
1)互斥同步
临界区:syncronized、ReentrantLock
信号量 semaphore
互斥量 mutex
2)非阻塞同步
CAS(Compare And Swap)
3)无同步方案
可重入代码
使用Threadlocal 类来包装共享变量,做到每个线程有自己的copy
线程本地存储
1)核心参数
public ThreadPoolExecutor(int corePoolSize, // 核心线程数量大小
int maximumPoolSize, // 线程池最大容纳线程数
long keepAliveTime, // 线程空闲后的存活时长
TimeUnit unit,
//缓存异步任务的队列 //用来构造线程池里的worker线程
BlockingQueue workQueue,
ThreadFactory threadFactory,
//线程池任务满载后采取的任务拒绝策略
RejectedExecutionHandler handler)
3)线程池大小分配
线程池究竟设置多大要看你的线程池执行的什么任务了,CPU密集型、IO密集型、混合型,任 务类型不同,设置的方式也不一样。
任务一般分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线 程池。
3.1)CPU密集型
尽量使用较小的线程池,一般Cpu核心数+1
3.2)IO密集型
方法一:可以使用较大的线程池,一般CPU核心数 * 2
方法二:(线程等待时间与线程CPU时间之比 + 1)* CPU数目
3.3)混合型 可以将任务分为CPU密集型和IO密集型,然后分别使用不同的线程池去处理,按情况而定
ThreadLocal变量是维护在Thread内部的,这样的话只要我们的线程不退出,对象的引用就会 一直存在。当线程退出时,Thread类会进行一些清理工作,其中就包含ThreadLocalMap, Thread调用exit方法如下:
ThreadLocal在没有线程池使用的情况下,正常情况下不会存在内存泄露,但是如果使用了线程 池的话,就依赖于线程池的实现,如果线程池不销毁线程的话,那么就会存在内存泄露。
启动类加载器:负责加载JRE的核心类库,如jre目标下的rt.jar,charsets.jar等
扩展类加载器:负责加载JRE扩展目录ext中JAR类包
系统类加载器:负责加载ClassPath路径下的类包
用户自定义加载器:负责加载用户自定义路径下的类包
为什么会有多种:
1)分工,各自负责各自的区块
2)为了实现委托模型
(New IO)为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提 供非阻塞式的高伸缩性网络。
特性:I/O多路复用 + 非阻塞式I/O
NIO适用场景
服务器需要支持超大量的长时间连接。比如10000个连接以上,并且每个客户端并不会频繁地发 送太多数据。例如总公司的一个中心服务器需要收集全国便利店各个收银机的交易信息,只需要 少量线程按需处理维护的大量长期连接。
Jetty、Mina、Netty、ZooKeeper等都是基于NIO方式实现。
1)引入了模块系统,采用模块化系统的应用程序只需要这些应用程序所需的那部分JDK模块, 而非是整个JDK框架了,减少了内存的开销。
2)引入了一个新的package:java.net.http,里面提供了对Http访问很好的支持,不仅支持 Http1.1而且还支持HTTP2。
3)引入了jshell这个交互性工具,让Java也可以像脚本语言一样来运行,可以从控制台启动 jshell ,在 jshell 中直接输入表达式并查看其执行结果。
4)增加了List.of()、Set.of()、Map.of()和Map.ofEntries()等工厂方法来创建不可变集合
5)HTML5风格的Java帮助文档
6)多版本兼容 JAR 功能,能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本。
7)统一 JVM 日志
可以使用新的命令行选项-Xlog 来控制JVM 上 所有组件的日志记录。该日志记录系统可以设置 输出的日志消息的标签、级别、修饰符和输出目标等。
8)垃圾收集机制 Java 9 移除了在 Java 8 中 被废弃的垃圾回收器配置组合,同时把G1设为默认的垃圾回收器实 现.因为相对于Parallel来说,G1会在应用线程上做更多的事情,而Parallel几乎没有在应用线程 上做任何事情,它基本上完全依赖GC线程完成所有的内存管理。这意味着切换到G1将会为应用 线程带来额外的工作,从而直接影响到应用的性能
9)I/O 流新特性 java.io.InputStream 中增加了新的方法来读取和复制 InputStream 中包含的数据。 readAllBytes:读取 InputStream 中的所有剩余字节。 readNBytes: 从 InputStream 中读取指定数量的字节到数组中。 transferTo:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中 。
1)Spring Boot可以建立独立的Spring应用程序;
2)内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再做 部署工作了;
3)无需再像Spring那样搞一堆繁琐的xml文件的配置;
4)可以自动配置Spring。SpringBoot将原有的XML配置改为Java配置,将bean注入改为使 用注解注入的方式(@Autowire),并将多个xml、properties配置浓缩在一个appliaction.yml 配置文件中。
5)提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功 能;
6)整合常用依赖(开发库,例如spring-webmvc、jackson-json、validation-api和tomcat 等),提供的POM可以简化Maven的配置。当我们引入核心依赖时,SpringBoot会自引入其 他依赖。
1)Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有 利于动态调整提供者权重。
2)RoundRobin LoadBalance
轮询,按公约后的权重设置轮询比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在 那,久而久之,所有请求都卡在调到第二台上。
3)LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
4)ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起 剧烈变动。
缺省只对第一个参数 Hash,如果要修改,请配置
缺省用 160 份虚拟节点,如果要修改,请配置
服务端并发限流:executes
客户端并发限流:actives
样例 1
限制 com.foo.BarService 的每个方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个:
样例 2
限制 com.foo.BarService 的 sayHello 方法,服务器端并发执行(或占用线程池线程数)不能 超过 10 个:
样例 3 限制 com.foo.BarService 的每个方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个:
或
样例 4
限制 com.foo.BarService 的 sayHello 方法,每客户端并发执行(或占用连接的请求数)不能 超过 10 个:
或
mysql复制主要有三种方式:基于SQL语句的复制(statement-based replication, SBR),基于行 的复制(row-based replication, RBR),混合模式复制(mixed-based replication, MBR)。对应 的,binlog的格式也有三种:STATEMENT,ROW,MIXED。
① STATEMENT模式(SBR) 每一条会修改数据的sql语句会记录到binlog中。优点是并不需要记录每一条sql语句和每一行的 数据变化,减少了binlog日志量,节约IO,提高性能。缺点是在某些情况下会导致master-slave 中的数据不一致( 如sleep()函数, last_insert_id(),以及user-defined functions(udf)等会出现 问题)
② ROW模式(RBR) 不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了,修改成什么样了。而且不会出 现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题。缺 点是会产生大量的日志,尤其是alter table的时候会让日志暴涨。
③ MIXED模式(MBR) 以上两种模式的混合使用,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT 模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择日志保存方 式。
乐观锁
用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即 为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实 现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加1。当我 们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比 对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期 数据。
悲观锁
在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中synchronized很 相似,共享锁(读锁)和排它锁(写锁)是悲观锁的不同的实现
共享锁(读锁)
共享锁又叫做读锁,所有的事务只能对其进行读操作不能写操作,加上共享锁后在事务结束之前 其他事务只能再加共享锁,除此之外其他任何类型的锁都不能再加了。
排它锁(写锁)
若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务 不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。
表级锁
innodb 的行锁是在有索引的情况下,没有索引的表是锁定全表的
行级锁
行锁又分共享锁和排他锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。 注意:行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级 锁。
1)基于XA协议的两阶段提交(2PC) XA 规范主要 定义了 ( 全局 ) 事务管理器 ( Transaction Manager ) 和 ( 局部 ) 资源管理器 ( Resource Manager ) 之间的接口。
2)两阶段提交 事务的提交分为两个阶段: 预提交阶段(Pre-Commit Phase) 决策后阶段(Post-Decision Phase)
3)补偿事务(TCC) 针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段
Try 阶段主要是对业务系统做检测及资源预留 Confirm 阶段主要是对业务系统做确认提交,Try 阶段执行成功并开始执行 Confirm 阶段时, 默认Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。 Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放
4)本地消息表(MQ 异步确保) 其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。
5)MQ 事务消息 有一些第三方的 MQ 是支持事务消息的,比如 RocketMQ,他们支持事务消息的方式也是类似 于采用的二阶段提交,但是市面上一些主流的 MQ 都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
6)Sagas 事务模型 该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程 中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
7)其他补偿方式 加入详细日志记录的,一旦系统内部引发类似致命异常,会有邮件通知。同时,后台会有定时任 务扫描和分析此类日志,检查出这种特殊的情况,会尝试通过程序来补偿并邮件通知相关人员。 在某些特殊的情况下,还会有 “人工补偿” 的,这也是最后一道屏障。