02_并发编程
2021-02-21 14:47:10 0 举报
AI智能生成
并发编程笔记
作者其他创作
大纲/内容
并发编程关键点
并发编程的核心在于压榨系统资源(引出进程、线程、协程)
所以从操作系统层面有进程,到每个应用层面有线程和协程(引出三者的区别以及模型)
进程、线程、纤程
https://blog.csdn.net/weixin_32265569/article/details/107574808
是什么
Process
Future
Fiber
KLT、LWP、UT之间的关系
原理
物理机的内存模型
java内存模型
应用
线程
协程
通过原理引出内存一致性问题(线程安全),通过锁解决同步问题
好的工具类
JUC
线程
Thread
start
sleep
yield
setDaemon
FutureTask
get
cancel(true)
Thread.currentThread().setPriority()
interrupt
isInterrupt
interrupted
Runable
void run()
Callable
<T> run()
Excutor
Executors
newFixedThreadPool(int i)
LinkedBlokingQueue
newSingleThreadExecutor
LinkedBlokingQueue
newScheduledThreadPool(int i)
DelayedWorkQueue
newCachedThreadPool()
SynchronousQueue
ThreadFactory
ExecutorService
excutor(Runable)
submit( callable )
Future<T>
get
cancel(true)
invokeAll( Collection<? extends Callable> tasks )
List<Future<T>>
shutdown
shutdownNow
CompletionService<T> comp = new ExecutorCompletionService<T>( ExcutorService es )
submit
take
UncaughtException
https://blog.csdn.net/pange1991/article/details/82115437
线程池大小
Ncpu (unmber of CPUs)
Ucpu(target CPU utilization,0 <= Ucpu <=1)
W/C (ratio of wait time time to compute time)
Nthreads = Ncpu * Ucpu * (1 + W/C)
W:等待时间
C:计算时间
W/C理解为一个线程执行时间的占比
如大于1:IO密集型
如 W=2,C=1
通过公式计算得出需要多开线程
如小于1:计算密集型
如 W=1, C=2。或极端情况 W=0,剩余的都是计算时间
通过公式计算得出需要少开线程
总结
这种情况说明对cpu的计算量不大,尽量多开线程“引号:多开线程不一定还是非要在处理IO型任务,可处理一些计算型的任务”,为了把cpu利用起来
这种情况说明对cpu的利用率很大,几乎消耗的时间都是cpu在计算。
针对IO密集型
公式只作为参考还是要根据实际执行的任务类型和系统硬件情况而定
并发编程开篇就讲到,多线程就是为了压榨系统资源,提高效率。
IO密集型任务,特点是时间都利用在了IO等待,导致cpu没有利用起来,所以通过公式得出的结果是多开线程。但是没有强调多开线程指的就是同一个线程池多开线程并且处理同一类的IO密集型任务,比如jdbc连接一共就10个,我们开100个线程也没有用。但是我们可以通过多个线程去处理其他计算型的任务。目的就是cpu别闲着。
计算密集型任务,特点是时间都利用到了cpu计算上,那么说明cpu的利用率很高,在不停的去计算。cpu的调度单元就是线程,如果处理一个线程都导致cpu很忙说明没有可压榨的地方了。反之创建太多线程的话,cpu需要不断切换线程反而降低了执行效率,还不如专心的处理完一个线程。所以通过公式得出如果一个线程里等待时间为零,那么线程数于cpu核数相同。
只有当任务相互独立时,为线程池或工作队列设置界限才是合理的。如果任务之间存在依赖性,那么有界的线程池或有界的队列就可能导致线程“饥饿”死锁问题。此时应该使用无界的线程池,例如newCachedThreadPool
锁
Object
wait
定义:当一个任务在方法里遇到了对wait的调用的时候,线程的执行将被挂起,对象上的锁被释放。
在wait期间当前线程是挂起的,对象锁是释放的。可以通过notify,notifyAll,或等待时间到期。任务从wait中恢复执行。
在wait期间当前线程是挂起的,对象锁是释放的。可以通过notify,notifyAll,或等待时间到期。任务从wait中恢复执行。
notify,notifyAll
使用notify而不是notifyAll是一种优化。使用notify时,在众多等待同一个锁的任务中只有一个会被唤醒,因此如果你希望使用notify,就必须保证被唤醒的是恰当的任务。
notifyAll更安全,并不是唤醒所有正在等待wait的任务。事实上,当notifyAll因某个特定锁而被调用时,只有等待这个锁的任务才会被唤醒
protected native Object clone()
public boolean equals(Object obj)
protected void finalize()
public final native Class<?> getClass()
public native int hashCode()
public String toString()
final
volatile
synchronized
锁升级过程
偏向锁
轻量级锁
重量级锁
hotspot源码
Unsafe
LockSupport
cas
aqs
ReentrantLock
lock
tryLock
lockInterruptibly
unlock
await
signal
signalAll
condition
ReadWriteLock
Semaphore
acquire
release
CountDownLath
CyclicBarrier
Exchanger
exchange
阻塞队列
BlokingQueue
LinkedBlockingQueue
LinkedBlockingDeque
ArrayBlockingQueue
PriorityBlockingQueue
DelayQueue
Delayed
implements
getDelay
compareTo
DelayedWorkQueue
SynchronousQueue
1.6算法比1.5算法吞吐量提高3倍
1.6算法比1.5算法吞吐量提高3倍
并发容器
ConcurrentHashMap
ConcurrentSkipListmap
同步容器
SynchronizedMap
原子类型
AtomicInterge
Atomic...
线程本地存储
ThreadLocal
造成活跃性的几个方面
不合理使用锁,造成活跃性危险
锁顺序死锁
如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。
10-1(并发编程实战)
动态锁顺序死锁
10-2(并发编程实战)
协作对象之间发生死锁
10-5(并发编程实战)
通过开放调用避免,不要在临界区内调用另一个同步方法
线程饥饿死锁
原因:
1.固定线程数量,如newFixedThreadPool( 1 )
2.任务之间依赖返回结果,如任务1等待任务2的返回结果
有界线程池,资源池与相互依赖的任务不能一起使用
避免:
1.尽量使用,如newCachedThreadPool
2.任务执行内容最好是独立的,尽量不要相互依赖
资源竞争死锁
线程T1持有资源A并等待资源B释放,线程T2持有资源B并等待资源A释放
死锁
活锁
活锁通常是由于过度的错误恢复代码造成的,将错误地将不可恢复的错误当做可修复的错误。
饥饿
多线程执行中有线程优先级这个东西,优先级高的线程能够插队并优先执行,这样如果优先级高的线程一直抢占优先级低线程的资源,导致低优先级线程无法得到执行,这就是饥饿。当然还有一种饥饿的情况,一个线程一直占着一个资源不放而导致其他线程得不到执行,与死锁不同的是饥饿在以后一段时间内还是能够得到执行的,如那个占用资源的线程结束了并释放了资源。
无锁
cas,又称自旋锁,(轻重里面)属于轻量级锁,(乐观悲观里面)属于乐观锁,不需要惊动内核的
无锁是重量级锁的互补,而不是造成活跃性问题的所在。
糟糕的响应性
极差的代码逻辑,或io阻塞
性能与可伸缩性
提升性能意味着用更少的资源做更多的事情。“资源”含义很广。对于一个给定的操作,通常会缺乏某种特定的资源,例如cpu时钟周期,内存,网络带宽,IO带宽,数据库请求,磁盘空间以及其他资源。当操作性能受到某种资源限制时,通常称这个操作为某种资源密集型操作,例如:cpu密集型,数据库密集型。
可伸缩性指的是,当增加计算资源时例如cpu、内存、IO带宽、存储容量等,程序的吞吐量或者处理能力能相应的增加。
阿姆达尔定律
Amdahl定律描述的是在增加计算资源的情况下,程序在理论上能够实现最高加速比
Speedup <= 1 / F + (1-F)/N
Speedup 加速比
F:必须串行任务占比
N:n个处理器
线程引入的开销
上下文切换
在程序中发生越多的阻塞,与cpu密集型的程序就会发生越多的上下文切换,增加了调度开销,降低了吞吐量
上下文切换在大多数通用的处理器中,上下文切换的开销相当于5000~10000个时钟周期,也就是几微秒
unix系统的vmstat命令,和win系统的perfmon工具都能查询上下文切换册数。
内存同步
syncronized、volatile
阻塞
减少锁竞争
在并发程序中,对可伸缩性的最主要的威胁就是独占方式的资源锁。
有三种法师可以降低锁的竞争程度
减少锁的持有时间
降低锁的请求频率
使用带有协调机制的独占锁,这些机制允许更高的并发性
缩小锁范围(快进快出)
减小锁粒度
一个大锁拆分成多个小锁
所分段
避免热点域
替代独占锁方法
ReadWriteLock
ReentrantLock
非独占锁
非阻塞锁
检测CPU利用率系统命令
unix:vmstat、mpstat
win:perfmon
0 条评论
下一页