算法通关-第 0 关:通关热身
准备和应对算法
平时如何刷算法
根据专题进行分类刷题
面试如何应对算法
理解问题,复述问题进而明确问题
进一步确认问题,大胆说出想法,逐步找到最优想法
初步设计,先写整体,再考虑边界
测试检验,评判性能,优化解法
数据结构与算法基础
数据结构类型
进度安排
体系脉络
时间和空间复杂度时间复杂度常数阶:一般顺序执行并且只执行一次的代码。
1234sum = sum + 1;sum = sum + 2;sum = sum + 3;sum = sum + 4;
线性阶:执行的次数随着问题规模是线性变化的。
123for (int i = 0; i < n; i++) { // todo}
平方阶:主要是双层嵌套循环。
12345for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { // todo 时间复杂度为 1 的程序 }}
对数阶
1234567int count = 1;int n ...
JUC-12-StampedLock
ReentrantReadWriteLock读写锁定义:一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。
它只允许读读共存,而读写和写写依然互斥,大多实际场景读读线程间并不存在互斥关系,只有读写线程或写写线程间的操作需要互斥。
一个ReentrantReadWriteLock同时只能存在一个写锁但是可以存在多个读锁,但不能同时存在写锁和读锁,即一个资源可以被多个读操作访问―或一个写操作访问,但两者不能同时进行。
特点
可重入
读写兼顾
锁降级
ReentrantReadWriteLock锁降级:将写入锁降级为读锁,锁的严苛程度变强叫做升级,反之叫做降级。
如果同一个线程持有写锁,在没有释放写锁的情况下,它还可以继续获得读锁。这就是写锁的降级,降级成为读锁
将写锁降级为读锁:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级为读锁
如果释放了写锁,那么就完全转换为读锁。
读锁升级到写锁是不可能的
如果有线程在读,写线程需要等待读线程释放锁后才能获取锁
写锁和读锁互斥
写锁和读锁是互斥的(是指线程间的互斥,当前线程可以获取到写锁 ...
JUC-11-AQS
AQS 理论介绍
源码
AQS
AQS(AbstractQueuedSynchronizer)是Java中的一个抽象基类,用于实现同步器(Synchronizer)。它是Java并发包中一些重要的同步工具的基础,如ReentrantLock、Semaphore、CountDownLatch等。AQS提供了一种底层机制,帮助开发者实现自定义的同步器,从而能够更灵活地管理多线程之间的竞争和协作。
同步器的概念:
同步器是一种用于控制多线程访问共享资源的机制,它通常包含两个重要的方法:acquire(获取锁)和release(释放锁)。这两个方法分别用于在多线程之间协调资源的访问,确保线程安全。
AQS的基本结构:
AQS的核心思想是维护一个等待队列(Wait Queue)和一个状态变量(State)。等待队列中的线程按照先进先出(FIFO)的顺序等待获取锁或资源。状态变量表示同步器的状态,不同的同步器可以定义不同的状态语义。
AQS的主要方法:
方法
说明
acquire(int arg)
用于获取锁或资源,如果无法获取,则将当前线程加入等待队列,然后自旋等待或阻塞 ...
JUC-10-Synchronized与锁升级
Synchronized性能变化
Java5前,只有synchronized重量级锁,如果竞争激烈,性能会下降
Java早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖底层的操作系统的MutexLock(系统互斥量)实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间。
Java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源。为了减少获得锁和释放锁所带来的性能消耗引入了轻量级锁和偏向锁。
WHY 每个对象都可以成为一个锁
Monitor是一个同步工具,常被描述为一个Java对象,它依赖于底层操作系统的MutexLock实现,操作系统实现线程之间的切换需要从用户态到内核态的转换是成本非常高的
Monitor
JVM中的同步基于进入和退出管程(Monitor)对象实现,每个对象实例都会有一个Monitor,Monitor可以和对象一起创建、销毁。Mon ...
JUC-9-Java对象内存布局和对象头
谈谈你对改代码的理解?
1Object obj = new Object();
对象在堆中内存布局
对象内部结构分为:对象头、实例数据、对齐填充(保证8个字节的倍数)。对象头分为对象标记(markOop)和类元信息(klassOop),类元信息存储的是指向该对象类元数据(Klass)的首地址。
对象头
对象标记 Mark Word
默认存储对象的HashCode、分代年龄和锁标志位等信息。这些信息都是与对象自身定义无关的数据。MarkWord被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据,它会根据对象的状态复用自己的存储空间,在运行期间MarkWord里存储的数据会随着锁标志位的变化而变化。
类元信息(类型指针)
对象指向它的类元数据的指针,虚拟机通过该指针确定这个对象是哪个类的实例
实例数据存放类的属性(Field)数据信息,包括父类的属性信息
对齐填充虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅是为字节对齐这部分内存按8字节补充对齐。
JUC-8-ThreadLocal
ThreadLocal 介绍
WHAT
ThreadLocal提供线程局部变量,这些变量与正常变量不同,每一个线程在访问ThreadLocal实例时都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事务ID)与线程关联起来。
WHY
每一个线程都有自己专属的本地变量副本,主要解决让每个线程绑定自己的值,通过使用get()和set()方法获取默认值或将其值更改为当前线程所存的副本的值从而避免了线程安全问题
Methods
栗子
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051class House { int saleCount = 0; public synchronized void saleHouse() { ++saleCount; } ThreadLocal<Integer&g ...









