AQS
AQSAQS是什么:AQS,是AbstractQueuedSynchronizer简称,直翻过来叫抽象的队列式同步器, 也可以称作队列同步器,它是java.util.concurrent.lock包下的一个工具类。直观来讲,AQS是Java提供的一个类,这个类是被Abstract修饰的,需要被子类继承。也就是说AQS是Java并发中用以解决多线程访问共享资源问题的同步机制的基本的框架(或者说是一种规范),为Java并发同步组件提供统一的底层支持。即,AQS是个为各个同步组件提供基本框架的一个抽象类。
AQS做了什么:提供解决同步问题的基础框架。AQS类内维护了一个volatile int型的变量state,用于表示同步状态(锁的释放与获取),同时提供了一些列诸如getstate、setstate、compareAndSetState的方法来管理该同步状态,这些方法是子类中需要重写的部分,并且,AQS提供了模板方法去调用这些重写的方法;另外,AQS用一个虚拟的CLH FIFO的双向队列来管理被阻塞的线程。
AQS的整体框架如下:
有颜色的为方法,没颜色的为属性。
框架共分为5层,从 ...
文件系统
文件系统将硬盘空间以块进行划分,每个文件占用其中的几个块,然后再通过一个文件控制块 FCB 记录每个文件占据的硬盘数据块。
在linux中,这个文件控制块就是inode,想要访问文件,就要先获取这个inode,然后在inode中查找文件数据块的索引表,根据索引中记录的硬盘地址信息来访问硬盘,读写数据。
inode的结构是固定的,只有15个索引,前12个索引直接指向数据块地址,第13个索引记录索引地址,即不直接指向数据块,而是指向一张索引表,每张索引表可以记录256个索引,访问这里的数据需要多一次查找。第 14 个索引记录二级索引地址,第 15 个索引记录三级索引地址。
RAIDRAID,即独立硬盘冗余阵列,将多块硬盘通过硬件 RAID 卡或者软件 RAID 的方案管理起 来,使其共同对外提供服务。
RAID 的核心思路其实是利用文件系统将数据写入硬盘中不同数据块的特性,将多块硬盘上的空闲空间看做一个整体,进行数据写入,也就是说,一个文件的 多个数据块可能写入多个硬盘。
RAID常用的方式有5种,分别是 RAID 0、RAID 1、RAID 10、 RAID 5 和 RAID 6。
...
索引下推
索引下推首先,MySQL的结构图如下:
其中,Server层所做的事情,就是负责SQL的语法解析,生成执行计划,并调用存储引擎层去执行数据的存储核检索,判断引擎返回的数据是否满足where条件。索引下推就是指将上层(Server层)负责的事情,交给了下层(引擎层)去处理。具体的查询区别如下:
在没有使用索引下推时:
存储引擎读取索引记录;
根据索引中的主键值,定位并读取完整的行记录;
存储引擎把记录交给Server层去检测该记录是否满足WHERE条件。
而使用了索引下推的查询过程:
存储引擎读取索引记录(不是完整的行记录);
判断WHERE条件部分能否用索引中的列来做检查,条件不满足,则处理下一行索引记录;
条件满足,使用索引中的主键去定位并读取完整的行记录(就是所谓的回表);
存储引擎把记录交给Server层,Server层检测该记录是否满足WHERE条件的其余部分。
这里的优化点在于,可以提前根据索引中的数据过滤掉一部分不满足的值,这一部分数据不需要回表查询,也不需要交由Server层去处理。
但是要注意,这个提前判断只能判断索引中存在的值,比如说where条件中有a ...
MySQL的最左前缀原则
最左匹配原则比如我们建立了一个(姓名,年龄)的索引,那么它可以用来匹配按名字的查找,但是按照年龄就不行。这个最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。
MySQL的索引中,有一种形式,叫做联合索引。即它可以创建多个字段联合起来的索引。语法如下:
1create index index_name on table_name(column_1, column_2, column_3)
这种联合索引的好处在于:
创建联合索引,相当于创建了column_1,(column_1,column_2),(column_1,column_2,column_3)这三个索引。但是实际上只存在一个索引。这可以大大减小插入数据时修改索引所消耗的时间。
存在联合索引时,如果只查询联合索引这些字段,可以省去回表的时间。
通过联合索引可以筛选出来更少的数据。比如说下面的查询:
1select from table where col1 = 1 and col2 = 2 and col3 = 3
如果只有第一个索引,那么查询完满足第一个条件的数据后,还需要回表去看是否满足第二个 ...
MySQL驱动加载
传统方法使用Java代码连接MySQL需要走以下流程(使用框架也要做对应的配置):
12345678910public static void main(String[] args) throws ClassNotFoundException, SQLException { String url = "jdbc:mysql://localhost:3306/db"; String username = "root"; String password = "root"; String driverClassName = "com.mysql.cj.jdbc.Driver"; // 注册驱动 Class.forName(driverClassName); // 获取连接 Connection connection = DriverManager.getConnection(url, username, password);}
这里边涉及到一个 ...
Spring事务失效场景
事务失效
写在前面
Spring的事务是通过AOP这种代理的方式实现的。
事务失效的场景有以下几个
1.访问权限问题java的访问权限主要有四种:private、default、protected、public,如果我们加注解的方法不是public,那么事务就会返回空。
12345678910@Servicepublic class UserService { @Transactional // 这里,方法不是public修饰,而spring要求被代理的方法必须是public private void add(UserModel userModel) { saveData(userModel); updateData(userModel); }}
2.方法用final修饰一般用final修饰的方法,都是在该方法不想被子类重写,但是如果将事务方法定义为final,则会导致问题。
123456789@Servicepublic class UserService { @Tra ...
可重复读隔离级别会出现幻读吗
放在最前面:InnoDB的默认事务隔离级别是可重复读
隔离级别MySQL的隔离级别包括四种:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。
读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
串行化,对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
前两种隔离级别,是一定会出现幻读。而可重复读隔离级别是采用MVCC,即一致性视图来解决不可重复读的问题。具体而言,就是采用快照的方式,事务开启的时候会有一个版本号,然后数据是否可见都是基于版本号来判断的,低于该版本号的数据可见,高于的都不可见。
而可重复读隔离级别下,如果事务A开启后,事务B做了一定的修改并提交事务,在 ...
Netty源码简单解读
注:仅仅是尝试看源码,锻炼一下看源码能力,理解并不一定正确。
Netty的服务端创建创建代码如下:
123456789101112131415161718192021222324public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); // 接收新连接线程,创建新的连接 NioEventLoopGroup boss = new NioEventLoopGroup(); // 对应读取数据的线程,用于处理数据读取以及业务逻辑处理 NioEventLoopGroup worker = new NioEventLoopGroup(); serverBootstrap .group(boss, worker) // 这里是接收一个Channel的类型,然后通过反射创建一个工厂类 .channel(NioServerSocketChannel.class) / ...
传统网络编程与NIO编程
传统网络编程首先看一段最传统的Java网络编程。
服务端代码如下:
12345678910111213141516171819202122232425262728293031323334public class IOServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8000); // 接收新连接的线程 new Thread(() -> { while (true) { try { // 阻塞方法获取新的连接 Socket socket = serverSocket.accept(); // 每一个新的连接都创建一个线程,负责读取数据 ...
单例模式的双重锁检查
一般的单例模式12345678910111213public class Singleton { private static Singleton uniqueSingleton; private Singleton() { } public Singleton getInstance() { if (null == uniqueSingleton) { uniqueSingleton = new Singleton(); } return uniqueSingleton; }}
但是在多线程情况下,可能会导致多个实例:
Time
Thread A
Thread B
T1
检查到uniqueSingleton为空
T2
检查到uniqueSingleton为空
T3
初始化对象A
T4
返回对象A
T5
初始化对象B
T6
返回对象B
这种场景,就会创建两次对象。
...