第一章 走入并行世界
关于并行的两个定律: Amdahl定律 和Gustafson定律 考虑方向不同
Java内存模型 JMM 原子性 可见性 有序性
第二章 Java并行程序基础
线程创建: new Thread(Runable).start()
线程终止: stop 方法 会立即释放锁,导致数据不一致问题,已经废弃
线程中断: interrupt() 设置中断标识,run方法中可以依靠这个做线程退出的逻辑处理Thread.interrupted()用来判断
等待和通知:wait()和notify()必须在synchronized中,涉及到锁的操作
挂起和继续执行:suspend 和 resume 已经废弃
等待线程结束和谦让:
如果一个线程A依赖另外一个线程B,那就在A中执行B.join() 即 A要等待线程B结束
yield()是让出CPU资源,但是还是会进行资源竞争.
volatile关键字:保证线程间的可见性和禁止执行重排(有序性)但是不能保证原子性 JMM
守护线程: daemon 后台线程,当用户线程都结束时,守护线程也会结束,如垃圾回收线程
第三章 JDK并发包
1.同步控制
1.1可重入锁 ReentrantLock
ReentrantLock lock = new ReentrantLock()
lock.lock() 加锁
lock.unlock解锁
对同一个线程而言,可以重复的加锁,也要解相同次数的锁才行
中断响应: lock.lockInterruptibly() 会对thread.interrupt()抛出异常,对其进行处理即可
限时申请锁: lock.tryLock(time,timeUnit.xxx) 在限定时间内申请获取锁,成功返回true 失败返回false
公平锁: ReentrantLock lock = new ReentrantLock(true) 默认非公平,公平锁需要维护一个线程申请锁的队列
1.2 重入锁好搭档: Conditon 条件
Condition condition = lock.newCondition()
condition的用法类似于object.wait() 和 object.notify()
await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或者signalAll()方法是,线程活重新获取锁,继续执行.
当前线程被中断时也能跳出等待.
awaitUninterruptibly() 和 await()类似,但是不会中断相应
singal()唤醒一个线程,singalAll()唤醒所有线程.
并发容器中使用两个condition实现队列的take和put的阻塞.见ArrayBlockingQueue源码
1.3 允许多线程同时访问: 信号量 Semaphore
Semaphore (int permits) 或者 Semaphore (int permits,boolean fair) 数量 和 公平
大家看方法就能猜如来如何使用了
acquire() 获取一个凭证
acquireUninterruptibly() 获取凭证对中断响应
tryAcquire() 不等待获取凭证 类似 tryLock()
tryAcquire(timeout,unit) 指定时间内获取凭证
release() 释放凭证
1.4 ReadWriteLock 读写锁
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock ();
Lock readLock = readWriteLock.readLock(); 读锁
Lock writeLock = readWriteLock.writeLock(); 写锁
读读不互斥,写写互斥,读写互斥.
1.5 CountDownLatch 倒计时器
这个很常见不就写了
1.6 循环栅栏: CyclicBarrier
和CountDownLatch类似但比其强大.
CyclicBarrier(int parties,Runnable barrieraction)
循环栅栏会阻塞 xxx.await()直到满足数目才让线程一起向下执行,且在此之前会执行一次 barrieraction中run方法
1.7 线程阻塞工具类: LockSupport
它可以在线程内任意位置让线程阻塞.
LockSupport.park() 可以阻塞当前线程,类似的还有parkNanos(),parkUntil()等实现一个闲时的阻塞
LockSupprot.unpark(Thread) 释放指定线程的阻塞
2. 线程复用:线程池
这个就不浪费笔墨了,依托底层 ThreadPoolExecutor实现了几种常用的线程池工具,大家应该都明白的.
线程池可以根据自己的需求进行相应的自定义处理:
ThreadPoolExecutor提供了beforeExecute() ,afterExecute(),terminated() 方法对其进行控制.
线程数量选取: Nthreads = Nepu * Ucpu * ( 1 + W/C) 即 cpu数量 * 目标Cpu使用率 *(1 + 等待时间与计算时间比)
分而治之:Fork/Join框架
ForkJoinPool.submit(ForkJoinTask) 提交任务 task.fork() 执行结束后 使用 task.join收集结果
3. JDK并发容器
1. 并发集合
ConcurrenHashMap
CopyOnWriteArrayList
ConcurrentLinkedQueue
BlockingQueue
ConcurrentSkipListMap
2. 高效读写队列:深度剖析 ConcurentLinkedQueue
主要通过CAS(比较交换)操作和对head及tail的算法实现高效的读写
3. 高效读取: 不变模式下的CopyOnWriteArrayList
任何读取操作没有锁相关操作,但是写入时候.会加锁,复制原来的集合写一个新集合后替换老集合.
4. 数据共享通道: BlockingQueue
前面说过其take和put的通过两个condition里相互通知实现阻塞.
5.随机数据结构:跳表(SkipList)
维护多层链表,链表分层,通过空间换时间的方法来实现高速的查找.