1.1.1 Java程序运行原理分析
1、class文件内容
魔数:0xCAFEBABE
版本:JDK版本
访问标志:类的访问修饰符(public)
常量池
当前类
超级类
接口
字段
方法
属性
2、JVM运行时数据区
**方法区**:类信息、常量、静态变量、编译后的代码
**堆内存**:创建的对象、OutOfMemoryError
**虚拟机栈**:多个线程
一个线程一个私有空间
一个线程执行多个方法
一个方法对应多个栈帧
栈内存默认最大是1M
为JAVA代码准备的
**本地方法栈**:与虚拟机栈功能类似
为Native方法准备的
**程序计数器**:当前线程字节码指令执行位置
栈帧:局部变量表(本地变量表)、操作数栈、动态链接、方法返回地址、附加信息
线程共享(主内存):方法区、堆内存
线程独占(工作区):虚拟机栈、本地方法栈、程序计数器
1.1.2线程状态
线程的6个状态
New:尚未启动的状态(没有调用start方法)
Runnable:CPU正在执行、可运行(等待CPU调动)
Blocked:线程阻塞等待锁(synchronized)
Waiting:等待(没有等待时间,比如wait/join)
Timed Waiting:具有指定等待时间的等待状态(比如sleep)
Terminated:终止线程(正常执行完/异常终止)
1.1.3线程中止
正确的线程中止方法:interrupt、标志位(控制循环的条件)
interrupt正常中止的情况:
线程在调用Object类的wait()、wait(long)、wait(long,int),
Thread类的join()、join(long,int)、sleep(long,int)方法时被阻塞,interrupt会生效,抛出InterruptedException异常
线程是被I/O或NIO中的Channel所阻塞,interrupt会生效(抛出对应的I/O或NIO异常)
1.1.4内存屏障和CPU缓存
1、CPU性能优化手段:
缓存(CPU的一级缓存的容量通常在32~4096KB)
CPU读数据时,先在L1(一级缓存)中寻找,再从L2寻找,再从L3寻找,然后是内存,再后是外存储器
CPU对缓存进行改动时,需要发通知给其他CPU
CPU在读写缓存时,要监听其他CPU发出的通知,保证缓存的一致性
运行时指令重排:
仅在单CPU自己执行时,能保证结果正确
遵守as-if-serial语义(为提高并行度,不会对存在数据依赖关系的操作做重排序)
2、内存屏障:
写内存屏障(强制写入主内存):在指令后插入Store Barrier,让写入缓存中的最新数据更新写入主内存
读内存屏障(强制读取主内存):在指令前插入Load Barrier,让高速缓存中的数据失效,强制重新从主内存加载数据
1.1.5线程通信
1、线程通信的2种机制
wait/notify机制(有顺序要求、等待时会释放锁)
wait:当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁
notify/notifyAll:方法唤醒一个或所有正在等待这个对象锁的线程
park/unpark机制(没有顺序要求、等待时不会释放锁)
park:等待(许可/令牌)
unpark:提供(许可/令牌)
在lambda表达式中,this就是调用当前方法的对象,lambda不会创建对象
2、两种机制的优缺点
wait/notify要求在同步关键字里使用(否则会抛出IllegalMonitorStateException(非法监视器状态异常)),免去死锁,但是一定要先调用wait,再调用notify,否则永久等待
park/unpark没有顺序要求,但是park不会释放锁,所以在同步代码中使用要注意
3、伪唤醒
指线程并不是因为notify、notifyAll、unpark等api调用而唤醒,是更底层原因导致的
wait方法正确的判断方式:
synchronized(obj){
while(<条件判断>)
obj.wait();
…//执行后续操作
}
park方法正确的判断方式:
while(<条件判断>)
LockSupport.park();
…//执行后续操作
1.1.6线程封闭
线程封闭:将数据封闭在线程中而避免使用同步的技术叫线程封闭
线程封闭具体体现:ThreadLocal、局部变量
ThreadLocal:每个线程都拥有自己独立的变量(副本)
局部变量(栈封闭):位于执行线程的栈中,其他线程无法访问这个栈
1.1.7线程池
代码参考Demo9
1、线程:java中是一个对象
操作系统的资源
创建、销毁需要时间
2、线程池:为了方便控制线程数量
3、线程池原理:
线程池管理器:用于创建并管理线程池(创建线程池、销毁线程池、添加新任务)
工作线程:线程池中线程(可循环执行任务)
任务接口(Runnable):规定了任务的入口、任务执行完后的收尾工作、任务的执行状态
任务队列:存放没有处理的任务(缓存机制)
4、线程池接口:
Executor:最上层的接口,定义了执行任务的方法execute
ExecutorService:继承了Executor,扩展了Callable、Future、关闭方法
ScheduledExecutorService:继承了ExecutorService,增加了定时任务相关的方法
5、线程池实现类:
ThreadPoolExecutor:基础、标准的线程池实现
ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor,实现了ScheduledExecutor中相关的定时任务的方法
6、Executors工具类:
newFixedThreadPool(int nThreads):核心线程数=最大线程数,无界队列
newCachedThreadPool():核心线程数=0,最大线程数=Integer.MAX_VALUE,无界队列
**SynchronousQueue**每次执行offer方法先判断有没有空闲线程,如果没有空闲线程offer方法失败,接着创建一个新的线程
newSingleThreadExecutor():只有一个线程的线程池,无界队列
newScheduledThreadPool(int corePoolSize):核心线程数由参数指定,最大线程数=Integer.MAX_VALUE,无界队列
7、任务execute过程:
线程池达到核心线程数量,就判断工作队列是否已满,如果已满,就判断是否达到最大线程数量,如果没达到,就建新线程执行任务
调用线程池的shutdown方法:不接收新的任务,等已在线程池的任务全部执行完后,再终止线程池
调用线程池的shutdownNow方法:立刻终止,不接收新的任务,返回等待任务数,线程池中未执行完的任务被终止(抛出interruptedException)
确定合适数量的线程:
计算型任务:cpu数量的1~2倍
IO型任务:根据具体的IO阻塞时长进行考量(tomcat默认最大线程数200)