jdk学习笔记
类加载
classloader
classloader就是负责加载类的一种对象。ClassLoader是抽象类。classloader通过Class的binary name定位和生成定义该类所需要的数据。经典的策略是将binary name转换成文件名,然后从文件系统读取.class文件
每个Class对象都包含一个classLoader引用,指向定义了该Class对象的类加载器
数组的Class对象不是由类加载器创建的,是由Java runtime自动创建
调用数组Class对象的getClassLoader方法返回的是其元素类型的类加载器,如果元素类型是基本类型,那么数组对象就没有类加载器
许多应用通过继承ClassLoader扩展了JVM动态加载类的方式
类加载器还负责定位资源。
类加载器通过委派模型取寻找class文件和资源文件。每个类加载器实例都有父类加载器,当类加载器被要求去搜索某个class或资源时,它会先维护父类加载器去找,找不到的话再自己找
并发加载类
内置类加载器
运行时内置的类加载器:
1.Bootstrap class loader
虚拟机内置的类加载器,通常是null,且没有父
2.Platform class loader
所有platform classes对于platform class loader都是可见的,可以作为类加载器实例的父
Platform classes包括Java SE platform APIs,它们的实现类和由platform class loader或其祖先定义的JDK-specific run-time classes
动态代理
juc
线程池
核心参数
corePool:核心线程池大小
maximumPool:最大线程池的大小
BlockingQueue:用来暂时保存任务的工作队列
RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和(达到了最大线程池大小且工作队列已满),execute方法将要调用的Handler
keepAliveTime:超出核心线程数量的空闲线程的生存时间
内部变量allowCoreThreadTimeOut:默认是false,表示核心线程即使空闲仍可生存;设置为true的话keepAliveTime也对空闲的核心线程有效
新任务到达时,如果线程池中线程数量未达到核心线程数,则创建新的线程去执行任务;如果线程池线程数量不超过最大线程数,且任务队列已满的情况下,创建新的线程执行任务
volatile
Java内存模型保证对于volatile字段,所有线程都会看到一致的值
如果一个volatile变量也同时被final关键字修饰,编译时会报错?
cpu部分术语
- 内存屏障
一组cpu指令,实现对内存操作的顺序限制 - 缓存行
cpu高速缓存中可以分配的最小存储单元。处理器填写缓存行时会加载整个缓存行,现代cpu需要执行击败第cpu指令 - 原子操作
不可中断的一个或一系列操作 - 缓存行填充
当cpu识别到从内存中读取操作数是可缓存的,cpu读取整个高速缓存行到适当的缓存 - 缓存命中
如果进行高速缓存行填充操作的内存位置仍然是下次cpu访问的地址时,cpu直接从缓存中读操作数 - 写命中
当cpu要将操作数写回内存时,先检查这个数据是否已经存在于缓存行中,如果存在一个有效的缓存行,则cpu将这个操作数写回到缓存行,而不是写回内存 - 写缺失
一个有效的缓存行被写入到不存在的内存区域
Lock前缀指令
对volatile修饰的共享变量进行写操作时,jvm会加上一条Lock前缀指令
Lock前缀的指令在多核处理器下会引发两件事:
- 将当前处理器缓存行的数据写回到系统内存
- 这个写回内存的操作会使在其他cpu里缓存了该内存地址的数据无效
为了提高处理速度,cpu不跟内存直接通信,而是将内存数据读入高速缓存后在操作,高速缓存的数据何时刷新回内存的时机并不确定。
对声明了volatile的变量进行写操作时,jvm会向cpu发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但在多处理器下,其他处理器的高速缓存还是存的旧值,因此要实现缓存一致性协议,让每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,如果过期,就让缓存行失效,下次要读缓存行中的数据时,重新到内存读取
volatile的实现原则
- Lock前缀指令会引起处理器缓存刷新回内存
不同处理器的处理机制:Lock信号锁总线;Lock信号锁缓存;锁定内存区域的缓存并写回内存 - 一个处理器的缓存刷新回内存会导致其他处理器的缓存无效
处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存数据在总线上保持一致。
如有些处理器通过嗅探一个处理器来检测其他处理器打算写内存,而这个地址当前处理共享状态,那么正在嗅探的处理器将使它的缓存行无效
LinkedTransferQueue通过volatile和追加字节的方式优化队列出队入队的性能
synchronized
3种使用形式
- 对于普通同步方法,锁是当前实例对象
- 对于类静态同步方法,锁是当前类的Class对象
- 对于同步代码块,锁是synchronized括号里配置的对象
原理
jvm基于进入和退出Monitor对象来实现方法同步和代码块同步,但实现细节不同
- 代码块同步
使用monitorenter和monitorexit指令实现 - 方法同步
也是使用这两个指令,但细节不详
任何对象都有一个monitor与之关联
锁的升级与对比
Java SE1.6中,锁一共4种状态,级别从低到高依次是无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,随着竞争情况逐渐升级。锁可以升级,不能降级,目的是为了提高获得锁和释放锁的效率
- 偏向锁
大多数情况,锁不存在多线程竞争,且总数由同一个线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁
当一个线程访问同步块并获取锁时&