Java线程学习笔记(5)

ThreadPoolExecutor是Executor的实现,通过构造方法可以快速构造需要的执行器,包括可以指定BlockingQueue的长度,当queue满了之后的回调方法,核心线程数,最大线程数以及最大超时时间。

线程切换基本原理

CPU调度是以时间片的方式进行的,线程被CPU调度的时候实际上是占用CPU的一段时间片。

当一个线程发生阻塞的时候,会将线程的状态进行保存,完成之后该线程会进入挂起状态,并被其他线程交换出去,从而让出CPU的时间片,其他需要被调度的线程会被CPU进行调度。

当线程被唤醒的时候,会先读取挂起之前保存的上下文信息,然后再被CPU进行调度。

一次上下文的切换开销在5000-10000个CPU时钟周期,而volatile只需要20-50个时钟周期,为了让CPU充分工作就要减少上下文的切换。

死锁

锁顺序容易导致死锁,且非常难排查。

解决方法

1、当需要获取多个对象的锁的时候可以通过System.identityHashCode的方法获取每个对象的Hash值,并且按照一定的顺序来获取锁,避免出现因锁顺序导致的死锁

2、尽量将一个对象的所有操作封装起来,对外不暴露所得信息,尽量收缩同步代码块的保护范围。

3、使用定时锁,避免永远阻塞

4、通过线程转储的方式分析,kill -3 PID

阿姆达尔定律

串行组件的比重会严重影响程序的并行性能,所以要尽量减少持有锁的时间

如何减少阻塞

1、锁分解
2、锁分段(ConcurrentHashMap默认有16把锁,每把锁负责一段数据的同步)
3、通常对象分配的开销比同步的开销更低
4、减少上下文的开销(单独的线程来处理特定业务,从而减少线程的切换)

显示锁

Lock

显示锁必要要在finally中手动将锁释放

ReentrantLock(重入锁)

是sychronized的功能强化版本,可以提供更多的功能,包括定时等,而sychronized获取不到锁会永远阻塞。

ReadWriteLock(读写锁)

可以支持一写多读,可以分别获取读锁和写锁。
写锁可以变读锁,但是读锁不能变写锁,否则会导致死锁。

条件队列

wait---将线程挂起,会释放锁,针对synchronized代码块
await---将线程挂起,会释放锁,针对condition,类似于wait,但是需要和Lock一起使用
sleep---将线程挂起,不会释放锁

Condition

一个Lock可以有多个Condition,通过newCondition的方式创建,所以在一个Lock中可以存在多个谓语条件。
condition1.await()
condition1.signal()
condition2.await()
condition2.signal()

一个Lock可以有任意个条件队列而synchronized只有一个条件队列。

AQS

同步器,如果是独占的怎实现tryAcquire和tryRelease,如果是可共享的则实现tryAcquireShare和tryReleaseShare

CAS

原子变量
compareAndSwap
通过比较在V内存出A的对象是否是参数中A的对象,如果是则替换成B对象。

你可能感兴趣的:(Java线程学习笔记(5))