《并发编程的艺术》-笔记


记录《Java 并发编程的艺术》部分知识点

第二章 并发机制的底层实现原理

volatile

synchronized

第三章 java内存模型 JMM

happen before

 用来阐述内存之间的可见性

JMM中一个操作对另一个操作可见,必须遵循HappenBefore

有关原则有

对一个线程的每个操作  happen-before  对该线程的后续操作

对一个锁的解锁     happen-before  随后对一个锁的加锁

对一个volitile域的写    happen-before  对这个volitile域的读

传递性

final

第五章 java中的锁

可重入锁 ReentrantLock

支持重进入的锁。 支持一个线程对资源的重复加锁。

Condition接口

 任意一个java对象,都拥有一组监视器方法(定义在Object上),主要包括wait(),nofify(),notifyAll()等;这些方法与Synchronized同步关键字配合可以实现等待/通知模式

condition与Lock配合,也可以实现等待/通知模式。

condition前置条件:调用Lock.lock()获取锁或调用Lock.newCondition()获取Condition对象

调用方式是condition.await()。

第六章 java并发容器和框架

ConcurrentHashMap的实现原理与使用

ConcurrentLinkedQueue

java中的阻塞队列

什么是阻塞队列

BlockingQueue

  • 队列满,阻塞插入元素的线程;
  • 队列空,获取元素的线程会等待队列变为非空

常用于生产者和消费者场景

方法/处理方式抛出异常返回特殊值一直阻塞超时退出
插入方式 add(e) offer(e) put(e) offer(e,time,unit)
移除方法 remove() poll() take() poll(time.unit)
检查方法 element() peek() 不可用 不可用

 

 

 

 

抛出异常

  队列满,在插入则会抛出异常;队列空,获取元素则抛异常;

返回特殊值

  插入成功返回true,取不到元素则返回null

一直阻塞

  队列满,阻塞生产者;队列空,阻塞消费者;

超时退出

  生产者对应的队列满了,阻塞一段时间后,会退出。

 

java里的阻塞队列

  • ArrayBlockingQueue:    一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue:  一个由链表结构组成的无界阻塞队列。
  • PriorityBlockingQueue:  一个支持优先级排序的无界阻塞队列。
  • DelayQueue:       一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:   一个不存储元素的阻塞队列。
  • LinkedTransferQueue:   一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:   一个由链表结构组成的双向阻塞队列。

 

  • ArrayBlockingQueue 有界阻塞队列 先进先出 new ArrayBlocingQueue(1000,ture) 则可以创建公平的阻塞队列,代价的降低吞吐量 
  • LinkedBlockingQueue 最大长度Integer.MAX_VALUE
  • PriorityBlockingQueue 默认采取升序,可以自定义实现。同优先级下的顺序不能保证。
  • DelayQueue 略
  • SynchronousQueue put必须等待take操作 构造函数true 则是公平访问。适合传递场景。吞吐量比linked和Array都高
  • ~

 

阻塞队列实现原理

通知模式

大致总结:

声明notFull和notEmpty两个Condition对象

put的生产方法里;会校验队列是否满了,满了则调用await();不满则insert,insert里调用notEmpty的signal()

take方法,队列空,则调用await()方法

 

await()的实现是调用了LockSupport的park()方法;

part()调用了Unsafe.park;

 

 

 

 

 

 

 

 

 

第九章 java中的线程池

 


 

线程池实现原理

 

图1

threadPoolExecutor执行示意图

图2

 


 

 

对于图2

1.如果当前运行的线程少于corePoolSize,则创建新线程来执行任务 需获取全局锁
2.如果运行的线程等于或者多与corePoolSize,则将任务加入BlockingQueue
3.如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务  需获取全局锁
4.如果创建新线程将使当前运行的线程超出maximumPoolSize(线程池的最大数量),任务将被拒绝,并调用rejectedExecution()方法

可核心线程数干活;干不完放到队列里;队列满了就找非核心线程干;非核心线程干不过来就执行丢弃策略。

尽可能避免获取全局锁

 

线程池的使用

new ThreadPoolExecutor(corePoolSize,maximumPoolSize, keepAliveTime, milliseconds, runnableTaskQueue, handler);

 

1.corePoolSize 线程池基本大小; 提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使还有空闲的线程能干。等到需要执行的任务数大于线程池基本大小就不再创建。

若调用prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。

2.runnableTaskQueue(任务队列)阻塞队列

  1.   ArrayBlockingQueue  -- 基于数组的有界,先进先出
  2.   LinkedBlockingQueue  -- 静态工厂方法 Executors.newFixedThreadPool()用的这个队列
  3.   SynchronousQueue     -- 静态工厂方法 Executors.newCachedThreadPool()用的这个队列
  4.   PriorityBlockingQueue  --具有优先级的无限阻塞队列

3.maximumPoolSize 使用了无界队列,则这个参数就没用了。

4.RejectedExecutionHandler(饱和策略):队列和线程池都满了。

  • AbortPolicy:直接抛出异常  [??b??t] 
  • CallerRunsPolicy:只用调用者所在的线程来运行任务。
  • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  • DiscardPolicy:不处理,丢弃掉。 [d??skɑ?d]

 

向线程池提交任务

  • execute() 无返回值
  • submit() 有返回值

关闭线程池

shutdown 或 shutdownNow来关闭线程池

原理是遍历工作线程,逐个调用interrupt方法。

shutdownNow是置线程为STOP,shutdown是置线程为SHUTDOWN,

线程池的合理配置

N(CPU数)

CPU密集型任务应配置尽可能小的线程 如配置N+1

IO密集型不是一直有任务,配置尽可能多的线程 如2N

混合型,

建议使用有界队列。

增加稳定性,防止内存溢出。

第十章 Executor框架

 

Executor框架简介

 https://www.cnblogs.com/study-everyday/p/6737428.html 【Java多线程】Executor框架的详解( 这个也是从书上总结的)

 

 

ThreadPoolExecutor详解

 

 

ScheduledThreadPoolExecutor详解

 

 

 

FutureTask详解