库存-并发学习笔记 (5)

只有一种场景会触发这种转换,就是线程等待 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之前性能不够好 + 容易膨胀为重量级锁

死锁问题无法破坏不可抢占条件

设计一个锁能解决不可抢占条件

能够响应中断

支持超时

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygwfg.html