进程和线程区别
进程独占内存空间,保存各自运行状态,相互不干扰,可以相互切换,
线程共享内存资源,相互切换更快,更加细粒度,进程内子任务可以并发进行
进程 资源分配最小单元
线程锁cpu调度最小单位
进程独立的,线程不是独立的
线程挂掉,整个进程都挂了
进程切换比线程切换开销大
java进程线程关系
- 进一步封装 进程和线程
- 运行一个程序会产生一个进程,进程包含一个线程
- 进程读应一个jvm实例,多个线程共享jvm主线程
- 主线程可以创建子线程,最后完成
Thread start run
start 创建一个新的子线程并启动
run 普通的方法调用
Thread Runnable 什么关系
Thread 实现了Runnable 接口的类,使得run支持多线程
因类单一继承原则,推荐多使用Runnable接口
run()参数传参数
- 构造函数
- 成员函数
- 回调函数
线程返回值
- 主线程等待法
- Thread join阻塞调用当前线程的线程
- callable Future 线程池
线程状态
- new 创建没启动
- 运行runnable running ready
- 无限期等待 不会分配cpu时间,需要显示唤醒
没有设置timeout的Object.wait() 方法
没有设置timeout的Thread.join()方法
LockSupport.pack()方法
- 期限等待
sleep
设置参数的wait join方法
- 阻塞 等待锁
- 结束
sleep wait区别
thread object
sleep任何地方使用
wait 只能在同步块使用
sleep只让出cpu不释放锁
wait 还会释放同步沪斥锁
notify notifyAll区别
notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。
yield
概念
简介
Thread.yield()方法作用是:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),
并执行其他线程。yield()做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其
他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。
但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:
yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状
态转到可运行状态,但有可能没有效果。
中断线程
- stop
- interrupt 通知
- 被阻塞,立即推出阻塞,抛出InterruptException异常
- 正常状态,中断标志设置为true 线程继续进行,不受影响
synchronized
线程安全
- 临界数据
- 多条线程共同操作这些共享数据
互斥锁特性
- 互斥
- 可见性
获取对象锁
- 同步代码块 小括号内的实例对象
- 同步非静态方法 锁是当前对象的实例对象
获取类锁的用法
- 同步代码块
- 同步静态方法
synchronized低层实现
- java对象头
- monitor
对象在内存布局
- 对象头
- 实例数据
- 对齐填充
对象头
- mark word
- class metadata address
重入 得到锁后再次申请仍然能够得到锁
早期很差
- 重量级锁 mutex lock实现
- 线程之间切换 用户态到核心态 开销大
java6后
- 自适应自旋转
- 偏向锁
- 轻量级锁
自选锁
- 共享数据锁定状态持续性时间短,切换线程不值得
- 线程忙等待,不让出cpu
- 锁长期使用性能变差
- preBlockSpin
自适应自旋锁
- 自旋次数不固定
- 由上一次自选时间拥有者状态来决定
锁粗化
- 扩大加锁范围
锁状态
- 无锁 偏向锁 轻量级别锁 重量锁
锁内存语义
- 线程释放锁,java内模型会把对应的本地内存共享变量刷新到主存
偏向锁 加减锁不需要cas 消耗很低 只有一个线程访问同步块或者同步方法的场景
轻量级锁 竞争的线程不会阻塞,提高响应速度 自旋 交替执行同步块或者同步方法的场景
ReentrantLock再如锁
- aqs实现
- 可重入
synchronized关键字 rentranlock类
JMM内存模型
- 存储当前方法的所在本地变量信息,本地变量对其他线程不可见
JMM中的主内存
存储Java实例对象
包括成员变量、类信息、常量、静态变量等
属于数据共享的区域,多线程并发操作时会引发线程安全问题
JMM中的工作内存
存储当前方法的所有本地变量信息,本地变量对其他线程不可见
字节码行号指示器、Native方法信息
属于线程私有数据区域,不存在线程安全问题
JMM与Java内存区域划分是不同的概念层次
JMM描述的是一组规则,围绕原子性,有序性,,可见性展开
相似点:存在共享区域和私有区域
主内存与工作内存的数据存储类型以及操作方式归纳
方法里的基本数据类型本地变量将直接存储在工作内存的栈帧结构中
引用类型的本地变量:引用存储在工作内存中,实例存储在主内存中
成员变量、static变量、类信息均会被存储在主内存中
主内存共享的方式是线程拷贝一份数据到工作内存,操作完成后刷新回主内存
指令重排序
指令重排序需要满足的条件
在单线程环境下不能改变程序运行的结果
存在数据依赖关系点的不允许重排序
无法通过happens-before原则推导出来的,才能进行指令的重排序
A操作的结果需要对B操作可见,则A与B存在happens-before关系
i=1;//线程A执行
j=i;//线程B执行
happens-before的八大原则
1.程序次序规划:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作:
2.锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作;
3.volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
4.传递规则:如果操作A先行发生于操作B,而操作B又发生于操作C,则可以得出A先行发生于操作C;
线程A先行发生于操作C;
5.线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作;
6.线程中断规则:对线程interrupt()方法的调用先行发生于此中断线程的代码检测到中断事件的发生;
7.线程终结规则:线程中所有的操作都先行发生与线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的放回值手段检测到线程已经终止执行;
8.对象终结规划:一个对象的初始化完成先行发生于他的finalize()方法的开始;
happens-before的概念
如果两个操作不满足上述任意一个happends-before规则,那么这两个操作就没有顺序的保障,JVM可以对这两个操作进行重排序;如果操作A happens-before操作B,那么操作A在内存上所做的操作对操作B都是可见的
volatile变量为何立即可见?
当写一个volatile变量时,JMM会吧该线程对应的工作内存中的共享变量刷新到主页内存中;
当读取一个volatile变量时,JMM会把该线程对应的工作内置为无效
volatile如何禁止重排优化
内存屏障(Memory Barrier)
1.保证特定操作的执行顺序
2.保证某些变量的内存可见性
通过哦插入内存屏障指令禁止在内存屏障前后的指令重新排序优化
强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本
volatile和synchronized的区别
1.volatile本质是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主村中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞主直到该线程完成变量操作为止
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法和类级别
3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量修改的可见性和原子性
4.volatile不会造成线程堵塞:synchronized可能会造成线程的阻塞
5.volatile标记的变量不会被编译优化;synchronized标记的变量可以被编译器优化
cas 乐观锁
- 原子更新操作
- 乐观锁
- cas操作失败
缺点
- 循环时间长 开销大
- 只能保证一个共享变量的原子操作
- aba atomicStampedReference
java线程池
Executors
Java通过Executors提供四种线程池,分别为:
1、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2、newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3、newScheduledThreadPool
创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。
4、newCachedThreadPoo
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
Fork/join
- 并行框架
JUC Excutor接口
- Executor 运行新任务的简单接口,任务提交和任务执行细节接偶
- Excuror
ThreadPoolExecutor
- corepoolsizee 核心线程数量
- 最大线程数
- 等待队列
- keepalivetime
- 创建新线程
handler 饱和策略
- 直接抛出异常
- 调用这所在线程
- 丢弃最前任务
- 直接丢弃任务
- 自定义
线程池状态
- Running
- ShUTDOWN
- STOP
- TIDYING
- TERMINATED
线程池代大小
- cpu密集 线程 = 核心数+1
- io 线程 = cpu核心 (1+平均等待时间/平均工作时间)