线程同步器AQS源码简析
线程同步器AQS底层实现(以公平锁为例)锁执行lock对象时,实际上是调用的Sync对象的方法,而Sync又继承自AbstractQueuedSynchronizer(队列同步器AQS)
既然是AQS中的Q是Queued,那么它自然需要维护一个队列
对于每个节点,他有几个比较重要的字段和方法
Prev和Next,显而易见是指向前序节点和后续节点的指针
status:表示节点不同的状态
thread:记录被封装到该节点内的线程
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//每个处于等待状态的线程都可以是一个节点,并且每个节点是有很多状态的static final class Node { //每个节点都可以被分为独占模式节点或是共享模式节点,分别适用于独占锁和共享锁 static final Node SHARED = new Node(); static final Node EXCLUSIVE = null; // ...
JUC笔记(一)
Java JUC 笔记(1)锁机制使用synchronized加锁时,一定是和某个对象关联的;
当带锁的方法反编译成字节码后,我们会看到有一个monitor enter指令和monitor exit指令,他们就分别对应加锁和释放锁; 每个对象都有一个monitor监视器与之对应的是,而monitor enter正是要获取该对象监视器的所有权,一旦某个对象的监视器被获取,其他对象就无法得到(唯一)
仔细看字节码中,其实有两个monitor exit; 如果程序是正常退出,会直接从第一个exit跳转回return, 若不是正常退出,就会从exit2出去,执行10~14行的异常处理程序,通过athrow抛出异常
重量级锁在JDK6之前,synchronized被称为重量级锁,monitor依赖于底层操作系统的lock来实现,而虚拟机上的线程是映射到原生系统的线程上,切换成本高。
对于每个对象,都有一个monitor与之关联,在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的:
123456789101112131415161718ObjectMon ...
Redis高级(2) - 多级缓存
Redis高级(2) —- 多级缓存传统的缓存的请求要经过Tomcat,性能收到Tomcat限制; 同时若Redis中不存在所需要的数据,还要去MySql中进行进一步的查找操作,也比较耗时; 设置后就可以采用多级缓存方案
JVM进程缓存我们把缓存分为两类:
分布式缓存,例如Redis:
优点:存储容量更大、可靠性更好、可以在集群间共享
缺点:访问缓存有网络开销
场景:缓存数据量较大、可靠性要求较高、需要在集群间共享
进程本地缓存,例如HashMap、GuavaCache:
优点:读取本地内存,没有网络开销,速度更快
缺点:存储容量有限、可靠性较低、无法共享
场景:性能要求较高,缓存数据量较小
CaffeineCaffeine是一个基于Java8开发的,提供了近乎最佳命中率的高性能的本地缓存库。目前Spring内部的缓存使用的就是Caffeine。
它的使用也很简单
缓存使用的基本API:
12345678910111213141516171819202122@Testvoid testBasicOps() { // 构建cache对象 Cache& ...
Redis高级(1) - 持久化
Redis高级 —- 持久化(1)RDBRDB就是Redis数据快照,其将Redis的数据本身保存到硬盘
如果直接使用save来执行RDB,会阻塞所有命令
建议使用bgsave,会开启子进程来执行RDB,避免主进程收到阻塞
RDB在Redis内部的使用redis.conf中配置了默认的RDB机制
1234567891011121314# 开启RDB# save "" 表示禁用RDB# 3600s内,若至少有1个key被修改,则执行bgsavesave 3600 1save 300 100save 60 10000# 是否压缩rdb文件rdbcompression yes# rdb文件名dbfilename dump.rdb# 文件保存的路径目录dir ./
RDB原理bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
fork采用的是copy-on-write技术:
当主进程执行读操作时,访问共享内存;
当主进程执行写操作时,则会拷贝一份数据,执行写操作。
AOF即追加文件,Redis处 ...
Redis秒杀代码(逐步测试修改)
Redis秒杀代码(逐步测试修改)
因为主要体现秒杀的业务,所以这里没融合jwt或者Session的登录校验,故id是直接显示传入的
秒杀代码.Ver1 (单例)本次秒杀没有通过redis,在单例的情况下,直接通过查询数据库来实现防止超卖和一人一单,最简单,最暴力
12345678910111213141516171819202122232425262728293031323334353637383940414243public Result seckillItem(SecKillDTO secKillDTO) { Long itemId = secKillDTO.getItemId(); Long userId = secKillDTO.getUserId(); //1.获取对应的秒杀对象 SeckillItem item = seckillItemService.getById(itemId); LocalDateTime now = LocalDateTime.now(); //2.判断是否在秒杀时间内 //2.1 不在秒杀时间 ...
Redis-秒杀笔记1
Redis秒杀全局唯一ID按照我们平常做的自增的id来做的数据库,存在许多问题
id规律性过于明显,可以对其进行破解;比如可以通过id的变化来判断商城的订单,通过id来爬取数据等
会受到单表数据量的限制
全局ID生成器现在我们就需要生成一个全局唯一的ID,这就需要全局ID生成器,其需要具有唯一性、高性能、高可用、递增性和安全性
为了增加ID的安全性,我们不可以直接使用Redis自增的数值,而是要拼接一些其他信息。(其实就是雪花算法)
初版秒杀代码12345678910111213141516171819202122232425262728293031323334@Override@Transactionalpublic Result seckillVoucher(Long voucherId) { // 1.查询优惠券 SeckillVoucher voucher = seckillVoucherService.getById(voucherId); // 2.判断秒杀是否开始 if (voucher.getBeginTime().isAfter ...