Java-学习笔记
2021-09-06 11:15:50 1 举报
AI智能生成
持续补充中
作者其他创作
大纲/内容
并发编程
Java并发编程基础
创建线程的几种方式
继承 Thread 类(Thead也实现了Runnable接口)
实现Runnable接口
Callable/Future
有返回值
线程的生命周期(状态)
NEW
初始状态,线程被构建,但是还没调用start()方法
RUNNABLE
运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中"
BLOCKED
阻塞状态,表示线程阻塞于锁
WAITING
等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)
TIME_WAITING
超时等待状态,不同于WAITING,它可以在指定的时间自行返回
TERMINATED
终止状态,表示当前线程已经执行完毕了
启动和终止
启动
start()启动
终止
不建议使用过期的suspend()、resume()和stop()
例如stop,在结束一个线程时并不会保证线程的资源正常释
放,因此会导致程序可能出现一些不确定的状态;相当于kill -9
放,因此会导致程序可能出现一些不确定的状态;相当于kill -9
优雅的终止
中断线程
interrupt()方法
判断是否被中断(返回True或False)
isInterrupted()
线程的复位(清除中断标识)
Thread.interrupted()
阻塞的方法,当抛出InterruptedException之前,会清除中断标识
synchronized
加锁的三种方式
普通方法
锁是当前实例对象
静态方法
锁是当前类的Class对象
方法块
锁是Synchronized括号里面的对象
如何存储
synchronized用的锁是存在对象头里的
任何一个对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。
monitorenter指令插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处。
锁的升级
无锁
偏向锁
轻量级锁
重量级锁
线程间通信
wait
表示持有对象锁的线程 A 准备释放对象锁权限,释
放 cpu 资源并进入等待状态。
放 cpu 资源并进入等待状态。
线程进入等待队列
notify
表示持有对象锁的线程 A 准备释放对象锁权限,通
知 jvm 唤 醒 某 个 竞 争 该 对 象 锁 的 线 程 X 。 线 程 A
synchronized 代码执行结束并且释放了锁之后,线程 X 直
接获得对象锁权限,其他竞争线程继续等待(即使线程 X 同
步完毕,释放对象锁,其他竞争线程仍然等待,直至有新
的 notify ,notifyAll 被调用)。
知 jvm 唤 醒 某 个 竞 争 该 对 象 锁 的 线 程 X 。 线 程 A
synchronized 代码执行结束并且释放了锁之后,线程 X 直
接获得对象锁权限,其他竞争线程继续等待(即使线程 X 同
步完毕,释放对象锁,其他竞争线程仍然等待,直至有新
的 notify ,notifyAll 被调用)。
唤醒的线程进入到同步队列,竞争锁
notifyAll
notifyall 和 notify 的区别在于,notifyAll 会唤醒所有竞争同一个对象锁的所有线程,当已经获得锁的线程
A 释放锁之后,所有被唤醒的线程都有可能获得对象锁权限
A 释放锁之后,所有被唤醒的线程都有可能获得对象锁权限
唤醒所有的线程进入到同步队列,竞争锁
注意
三个方法都必须在 synchronized 同步关键字 所 限 定 的 作 用 域 中 调 用 , 否 则 会 报 错
java.lang.IllegalMonitorStateException ,意思是因为没有同步,所以线程对对象锁的状态是不确定的,不能调用这些方法。
java.lang.IllegalMonitorStateException ,意思是因为没有同步,所以线程对对象锁的状态是不确定的,不能调用这些方法。
能保证原子性、可见性
Java内存模型
volatile
可见性
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来从主内存中读取共享变量
通过内存屏障实现
硬件层面了解可见性
CPU高速缓存(L1、L2、L3)
通过高速缓存的存储交互很好的解决了处理器与内存的速
度矛盾,但是也为计算机系统带来了更高的复杂度,因为
它引入了一个新的问题,缓存一致性
度矛盾,但是也为计算机系统带来了更高的复杂度,因为
它引入了一个新的问题,缓存一致性
缓存一致性
总线锁
缓存锁
是基于缓存一致性协
议来实现的(MESI)
议来实现的(MESI)
CPU 层面的内存屏障
总的来说,内存屏障的作用可以通过防止 CPU 对内存的乱
序访问来保证共享数据在多线程并行执行下的可见性
序访问来保证共享数据在多线程并行执行下的可见性
总结:导致可见性问题的根本原因是缓存以及重排序
Java内存模型(JMM)
什么是JMM
重排序
编译器优化重排序
指令级并行重排序
内存系统重排序
JMM通过内存屏障指令来禁止特性类型的处理器重排序
JMM属于语言级的内存模型,通过禁止特性类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。
happens-before
简介
用来阐述操作之间的内存可见性。JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。
定义
1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前
2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)
规则
程序顺序规则
一个线程中的每个操作,happens-before于该线程中的任意后续操作
监视器锁规则
对于一个锁的解锁,happens-before于随后对这个锁的加锁
volatile变量规则
对于一个volatile域的写,happens-before于任意后续对这个volatile域的读
传递性规则
如果A happens-before B,且 B happens-before C, 那么 A happens-before C
start()规则
如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B的任意操作
join()规则
如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before 于线程A从ThreadB.join()操作成功返回。
Java中的锁
Lock
比synchronized更加灵活
本质上是个接口,定义了释放锁和获得锁的抽象方法,定义了锁的标准规范。有很多锁的实现,如: ReentrantLock、ReentrantReadWriteLock
AQS(AbstractQueuedSynchronizer同步队列)
是用来构建锁或者其他同步组件的基础框架,使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作
独占锁
tryAcquire
独占式获取同步状态,CAS设置同步状态
tryRelease
独占式释放同步状态
总结:在获取同步状态时,同步器维护一个FIFO的双向队列,获取状态失败的线程会被加入到队列中并在队列中进行自旋;移除队列或停止自旋的条件是前驱节点为头结点且本节点成功获取了同步状态。在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒头结点的后续节点。
应用:重入锁,读写锁中的写锁
共享锁
tryAcquireShared
共享式获取同步状态, 返回值>=0则成功,反之失败。
tryReleaseShared
共享式释放同步状态
总结:跟独占的流程是一样的,区别是:1、获取同步状态的成功是返回值>=0; 2、释放同步状态时,必须保证线程安全,一般是通过循环和CAS来保证的,因为释放同步状态的操作会同时来自多个线程。
应用:Semaphore
独占式超时获取同步状态
独占式超时获取同步状态doAcquireNanos(int arg, long nanosTimeout)和独占式获取同步状态在流程上非常相似,
其主要区别在于未获取到同步状态时的处理逻辑。acquire(int args)在未获取到同步状态时,将会使用当前线程一直处于等待状态,
而doAcquireNanos会使当前线程等待nanosTimeout纳秒,如果当前线程在nanosTimeout纳秒内没有获取到同步状态,
将会从等待逻辑中自动返回
其主要区别在于未获取到同步状态时的处理逻辑。acquire(int args)在未获取到同步状态时,将会使用当前线程一直处于等待状态,
而doAcquireNanos会使当前线程等待nanosTimeout纳秒,如果当前线程在nanosTimeout纳秒内没有获取到同步状态,
将会从等待逻辑中自动返回
CAS进行设置同步状态,CAS设置尾节点
ReentrantLock(重入锁)
重入锁,表示支持重新进入的锁,也就是说,如果当前线程 t1 通过调用 lock 方
法获取了锁之后,再次调用 lock,是不会再阻塞去获取锁的,直接增加重试次数
就行了。synchronized 和 ReentrantLock 都是可重入锁。
法获取了锁之后,再次调用 lock,是不会再阻塞去获取锁的,直接增加重试次数
就行了。synchronized 和 ReentrantLock 都是可重入锁。
公平锁NofairSync
不管当前队列上是否存在其他
线程等待,新线程都有机会抢占锁。上来就会cas尝试获取锁
线程等待,新线程都有机会抢占锁。上来就会cas尝试获取锁
非公平锁FailSync(默认)
表示所有线程严格按照 FIFO 来获取锁
区别:公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。非公平锁开销更小。
ReentrantReadWriteLock(读写锁)
读读不互斥,读写/写写互斥。适用读多写少的场景
LockSupport工具类
用于阻塞或唤醒一个线程。
park()
阻塞当前线程
unpark(Thread thread)
唤醒处于阻塞状态的线程
Condition接口
可以对比synchronized配合wait、notify/notifyAll实现等待/通知;
Lock配合Condition可以实现等待/通知模式
Lock配合Condition可以实现等待/通知模式
区别:condition支持多个等待队列,
支持在等待状态中不响应中断(awaitUninterruptibly());
支持线程进入等到状态到将来的某个时间(awaitUntil())
支持在等待状态中不响应中断(awaitUninterruptibly());
支持线程进入等到状态到将来的某个时间(awaitUntil())
方法
Condition condition = lock.newCondition()
await()
唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁
唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中,竞争锁
当前线程释放锁并在此等待,直到被signal通知或中断
当前线程进入等待队列并释放锁,线程状态变为等待状态
signal()
signalAll()
唤醒所有等待在Condition上的线程
对等待队列中的每个节点均执行一次signal()方法,所有节点全部移动到同步队列中,并唤醒每个节点的线程
注意: await()、signal()、signalAll()方法必须在lock.lock()和lock.unlock()中间使用;跟wait、notify一样,在获得锁的情况下使用;
实现原理:每个Condition对象都包含着一个FIFO的队列,等待队列;
Lock(同步器)拥有一个同步队列和多个等待队列;
Object监视器模型上,一个对象拥有一个同步队列和一个等待队列;
Lock(同步器)拥有一个同步队列和多个等待队列;
Object监视器模型上,一个对象拥有一个同步队列和一个等待队列;
Java并发容器和框架
ConcurrentHashMap
线程安全且高效的HashMap
锁分段技术:首先将数据分成一段一段地存 储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数 据也能被其他线程访问。
ConcurrentLinkedQueue
阻塞队列
Fork/Join框架
Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干 个小任务,最终汇总每个小任务结果后得到大任务结果的框架
原子操作类(12个)
原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式
原子更新基本类型
AtomicBoolean
AtomicInteger
AtomicLong
原理:自旋、CAS
原子更新数组
AtomicIntegerArray
原子更新整形数组里的元素
AtomicLongArray
原子更新长整型数组里的元素
AtomicReferenceArray
原子更新引用类型数组里的元素
原子更新引用
AtomicReference
原子更新引用类型
AtomicReferenceFieldUpdater
原子更新引用类型的字段
AtomicMarkableReference
原子更新带有标记为的引用字段
原子更新属性(字段)
AtomicIntegerFieldUpdater
原子更新整型的字段的更新器
AtomicLongFieldUpdater
原子更新长整型字段的更新器
AtomicStampedReference
原子更新带有版本号的引用类型,可以解决使用CAS进行原子更新可能出现的ABA问题
并发工具类
CountDownLatch
允许一个或多个线程等待其他线程完成操作;
new CountDownLatch(n)
计数器,等待n次
countDown()
n-1
await()
阻塞当前线程,直到n=0
实现原理:AQS共享锁
Semaphore(信号量)
用来控制同时访问特定资源的线程数量。用于流量控制,特别是公用资源有限的应用场景。
new Semaphore(n)
只允许n个线程并发执行
acquire()
获得许可证
release()
归还许可证
tryAcquire()
尝试获取许可证
实现原理:AQS共享锁
CyclicBarrier
可循环使用的屏障,让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
new CyclicBarrier(n)
拦截n个线程
await()
阻塞,直到n个线程调用await()方法
new CyclicBarrier(int parties, Runnable barrierAction)
线程都到达屏障时,优先执行barrierAction线程
实现原理:基于ReentrantLock 和 Condition 的组合使
用
用
线程池(ThreadPoolExecutor)
优点:
降低资源消耗
提高响应速度
提高线程的可管理性
核心参数
new ThreadPoolExecutor(
corePoolSize(线程池的基本大小)
maximumPoolSize(线程池最大数量)
keepAliveTime(线程活动保持时间)
TimeUnit(线程活动保持时间的单位)
BlockingQueue(任务队列)
ArrayBlockingQueue
基于数组结构,按FIFO排序排序
LinkedBlockingQueue
一个基于链表结构的阻塞队列,按FIFO排序元素,吞吐量高于ArrayBlockingQueue。
静态工厂 方法Executors.newFixedThreadPool()使用了这个队列
静态工厂 方法Executors.newFixedThreadPool()使用了这个队列
SynchronousQueue
一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,
否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,
静态方法工厂Executors.newCachedThreadPool使用了这个队列
否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,
静态方法工厂Executors.newCachedThreadPool使用了这个队列
PriorityBlockingQueue
一个具有优先级的无限阻塞队列
ThreadFactory(用于设置创建线程的工厂)
RejectedExecutionHandler(饱和策略)
AbortPolicy(默认这个)
直接抛出异常
CallerRunsPolicy
只用调用者所在线程来运行任务
DiscardOldestPolicy
丢弃队列里最近的一个任务,并执行当前任务(队列是FIFO,所以丢弃的是最先入队的任务)
DiscardPolicy
不处理,丢弃掉
提交任务
executor()
提交不需要返回值的任务
submit()
用于提交有返回值的任务,返回Future对象,通过future.get()获取返回值
关闭线程池
shutdown()
shutdownNow()
Executor框架
ThreadPoolExecutor
FixedThreadPool
固定线程数的
适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场 景,它适用于负载比较重的服务器。
无界队列,一直在用corePoolSize个线程在跑任务
SingleThreadExecutor
单个线程的
适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多 个线程是活动的应用场景。
CachedThreadPool
会根据需要创建新线程
CachedThreadPool是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者 是负载较轻的服务器。
maximumPoolSize被设置为 Integer.MAX_VALUE,把keepAliveTime设置为60s 如果没有空闲线程,则来一个任务创建一个线程,不用就很快销毁
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor
包含若干个线程
适用于需要多个后台线程执行周期任务,同时为了满足资源 管理的需求而需要限制后台线程的数量的应用场景
SingleThreadScheduledExecutor
只包含一个线程
适用于需要单个后台线程执行周期任务,同时需要保证顺 序地执行各个任务的应用场景
Future接口
Runnable接口和Callable接口
技术栈
分支主题
分支主题
性能优化
MySQL
Java基础
0. 设计模式
7大设计原则
工厂模式
简单工厂模式
工厂方法模式
抽象工厂模式
单例模式
原型模式
代理模式
静态代理
动态代理
JDK动态代理
CGLib动态代理
委派模式
策略模式
模板模式
适配器模式
装饰者模式
观察者模式
1. 数据结构
源码
Spring
简化开发
面向Bean(BOP)
IOC容器-通过配置文件或者注解的方式来管理对象之间的依赖关系
控制反转IOC
依赖注入DI
构造注入
set注入
依赖查找
因为依赖查询使用频率过低,不久就被 Spring 移除
了
了
面向切面AOP
源码
IOC容器初始化
定位、加载、注册
XmlBeanDefinitionReader解析配置文件,得到BeanDefinition对象,存入HashMap
DI
分为两步:实例化、依赖注入
AOP
选择代理策略、调用代理方法、触发通知
MVC
初始化九大组件、运行调用
面试题
Spring事务
概念
事务的传播属性
Spring 中的隔离级别
设计模式
MyBatis
四个对象
SqlSessionFactoryBuiler
SqlSessionFactoryBuiler
SqlSession
Mapper
应用分析与最佳实践
分页
物理分页(PageHelper)
limit
基于Mybatis的拦截器实现,插件
逻辑分页(RowBounds)
假分页(查出所有。在内存中分页)
体系结构与工作原理
缓存
一级缓存(本地缓存)
在会话(SqlSession)层面进行缓存,默认开启
二级缓存
namespace 级别 的,可以被多个 SqlSession 共享
第三方缓存做二级缓存
ehcache、redis等
源码解读
配置解析过程
完成了 config 配置文件、Mapper 文件、Mapper 接口上的注 解的解析,生成Configuration对象
会话创建过程
创建会话的过程,我们获得了一个 DefaultSqlSession,里面包含了一个 Executor,它是 SQL 的执行者。
三种Executor
获取Mapper对象
获得 Mapper 对象的过程,实质上是获取了一个 MapperProxy 的代理对象。 MapperProxy 中有 sqlSession、mapperInterface、methodCache。
执行SQL
插件原理
设计模式
工厂模式
SqlSessionFactory、MapperProxyFactory
代理
MapperProxy、Plugin插件
装饰器
LoggingCache、LruCache 等对 PerpectualCache 的装饰; CachingExecutor 对其他 Executor 的装饰
责任链
InterceptorChain
模板方法
BaseExecutor 与子类 SimpleExecutor、BatchExecutor、ReuseExecutor
单例模式
SqlSessionFactory、Configuration
0 条评论
下一页