只有一种场景会触发这种转换,就是线程等待 synchronized 的隐式锁。synchronized 修饰的方法、代码块同一时刻只允许一个线程执行,其他线程只能等待,这种情况下,等待的线程就会从 RUNNABLE 转换到 BLOCKED 状态。而当等待的线程获得 synchronized 隐式锁时,就又会从 BLOCKED 转换到 RUNNABLE 状态
RUNNABLE 与 WAITING 的状态转换
第一种场景,获得 synchronized 隐式锁的线程,调用无参数的 Object.wait() 方法
第二种场景,调用无参数的 Thread.join() 方法
第三种场景,调用 LockSupport.park() 方法。其中的 LockSupport 对象,也许你有点陌生,其实 Java 并发包中的锁,都是基于它实现的
RUNNABLE 与 TIMED_WAITING 的状态转换
调用带超时参数的 Thread.sleep(long millis) 方法
获得 synchronized 隐式锁的线程,调用带超时参数的 Object.wait(long timeout) 方法
调用带超时参数的 Thread.join(long millis) 方法
调用带超时参数的 LockSupport.parkNanos(Object blocker, long deadline) 方法
调用带超时参数的 LockSupport.parkUntil(long deadline) 方法
从 NEW 到 RUNNABLE 状态
Java 刚创建出来的 Thread 对象就是 NEW 状态,而创建 Thread 对象主要有两种方法
RUNNABLE 到 TERMINATED 状态
正常结束
interrupt()
10 | Java线程(中):创建多少线程才是合适的?笔记
为什么要使用多线程?
使用多线程,本质上就是提升程序性能。
两个指标
延迟指的是发出请求到收到响应这个过程的时间;延迟越短,意味着程序执行得越快,性能也就越好。
吞吐量指的是在单位时间内能处理请求的数量;吞吐量越大,意味着程序能处理的请求越多,性能也就越好
本质上就是将硬件的性能发挥到极致
多线程的应用场景
I/O密集型
CPU密集型
评论区
个人觉得公式话性能问题有些不妥,定性的io密集或者cpu密集很难在定量的维度上反应出性能瓶颈,而且公式上忽略了线程数增加带来的cpu消耗,性能优化还是要定量比较好,这样不会盲目,比如io已经成为了瓶颈,增加线程或许带来不了性能提升,这个时候是不是可以考虑用cpu换取带宽,压缩数据,或者逻辑上少发送一些。最后一个问题,我的答案是大部分应用环境是合理的,老师也说了是积累了一些调优经验后给出的方案,没有特殊需求,初始值我会选大家都在用伪标准
11 | Java线程(下):为什么局部变量是线程安全的?笔记
为什么局部变量不存在线程安全问题
从线程栈解释:局部变量在线程的独享栈中
没有共享就没有伤害
线程安全问题的解决方案之一
线程封闭
ThreadLocal
注意ThreadLocal的内存泄漏问题
总结
new出来的对象都在堆中的合理解释
对象在堆中,但是对象的句柄(引用或者指针)在栈中
没有共享就没有伤害
递归注意深度,容易导致栈内存溢出
ThreadLocal的内存泄漏问题
Spring对数据源连接池的抽象 ThreadLocal实现的
12 | 如何用面向对象思想写好并发程序?笔记
三个思路
封装共享变量
将共享变量作为对象属性封装在内部,对所有公共方法制定并发访问策略
识别共享变量间的约束条件
public class SafeWM { // 库存上限 private final AtomicLong upper = new AtomicLong(0); // 库存下限 private final AtomicLong lower = new AtomicLong(0); // 设置库存上限 void setUpper(long v){ upper.set(v); } // 设置库存下限 void setLower(long v){ lower.set(v); } // 省略其他业务代码 } * 约束条件,决定了并发访问策略 * 忽略了一个约束条件:下限 < 上限 * 不安全的一个例子 public class SafeWM { // 库存上限 private final AtomicLong upper = new AtomicLong(0); // 库存下限 private final AtomicLong lower = new AtomicLong(0); // 设置库存上限 void setUpper(long v){ // 检查参数合法性 if (v < lower.get()) { throw new IllegalArgumentException(); } upper.set(v); } // 设置库存下限 void setLower(long v){ // 检查参数合法性 if (v > upper.get()) { throw new IllegalArgumentException(); } lower.set(v); } // 省略其他业务代码 } 当setUpper(5) 和 setLower(7)同时发生时,会发生upper = 5 lower = 7 * 着重注意if else语句造成的竞态条件制定并发访问策略
避免共享
不变模式(不可变对象/变量)
管程/并发工具(JUC)
优先考虑java的并发包,一般能解决绝大多数并发问题
迫不得已时再考虑“低级”原语:synchronized、Lock、Semaphore,使用时千万小心
避免过早优化,首先保证安全,等到确实遇到性能瓶颈的时候,再考虑优化
14 | Lock和Condition(上):隐藏在并发包中的管程笔记
并发编程的两个核心问题
互斥-同一个时刻只能有一个线程可以访问共享资源
同步-线程之间如何通信、协作
Java中管程的两个实现--java线程的两种协作方式
synchronized + wait + notify
synchronized实现互斥
wait + notify实现同步
lock + Condition
lock实现互斥
condition实现同步
再造管程的理由
synchronized存在的问题
1.5之前性能不够好 + 容易膨胀为重量级锁
死锁问题无法破坏不可抢占条件
设计一个锁能解决不可抢占条件
能够响应中断
支持超时