1.1.2 测试上下文切换次数和时长
可以度量上下文切换的工具
- 使用
Lembench3可以测量上下文切换的时长 - 使用
vmstat可以测量上下文切换的次数。
1.1.3 如何减少上下文切换
减少上下文切换的方法有无锁并发编程、CAS算法、使用最小线程和使用协程。
- 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
- CAS算法。Java的Atomic包使用CAS算法来更新数据,而且不需要加锁。
- 使用最小线程。避免创建不需要的线程。
- 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
1.2 死锁
避免死锁的方法
- 避免一个线程同时获取多个锁
- 避免一个线程在所内同时占用多个资源,尽量保证每个锁只占用一个资源
- 尝试使用定时锁,使用lock.tryLock 来替代内部锁机制。
- 对应数据库锁,加锁和解锁必须在一个数据库连接里,否则或出现解锁失败情况。
1.4 小结
对于Java工程师,建议多使用JDK并发包提供的并发容器和工具来解决并发问题,因为这些类都已经通过了充分的测试和优化
2.2.2 锁的升级与对比
这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。
2.3 原子操作的实现原理
3 Java内存模型
3.1.2 Java内存模型的抽象结构
再Java中,所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。局部变量,方法定义参数和异常处理器参数不会在线程之间共享,他们不会有内存可见性问题,也不受内存模型的影响。
3.6 final域的内存语义
3.6.1 final域的重排序规则
对于final域,编译器和处理器要遵守两个重排序规则。
- 对于构造函数内对一个final域的写入,与随后把这个构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
- 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能排序。
6.3 Java中的阻塞队列
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加操作支持阻塞的插入和移除方法。
- 支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的过程,知道队列不满
- 支持阻塞的移除方法:当队列为空时,获取元素的线程会等待队列变为非空。
| 方法/处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
|---|---|---|---|---|
| 插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
| 移除方法 | remove(e) | poll() | take() | poll(time,unit) |
| 检查方法 | element() | peek() | 不可用 | 不可用 |
- 抛出异常:当队列满时,如果在往队列里面插入元素,会抛出IllegalStateException(“Queue full”)异常,如果队列为空,从队列获取元素会抛出NoSuchElementException异常。
- 返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true,如果是从队列取出一个元素,如果没有则返回null。
如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put和offer方法永远不会被阻塞,而且使用offer方法,该方法永远返回true。
6.3.2 Java里的阻塞队列
JDK7提供了7个阻塞队列
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列
- LinkedBlockingQueue:一个由链表组成的有界阻塞队列
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列
- DelayQueue:一个使用优先级队列实现的无界阻塞队列
- SynchronousQueue:一个不存储元素的阻塞队列
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
- LinkedBlockingDeque:一个由链表组成的双向阻塞队列。
8 Java中的并发工具类
8.1 等待多线程完成的CountDownLatch
CountDownLatch运行一个或多个线程等待其他线程完成操作。
import java.util.concurrent.CountDownLatch;public class CountDownLatchTest {static CountDownLatch c = new CountDownLatch(2);public static void main(String[] args) throws InterruptedException {new Thread(() -> {System.out.println(1);c.countDown();System.out.println(2);c.countDown();}).start();//不要写成 wait()c.await();System.out.println(3);}}
8.2 同步屏障CyclicBarrier
它要做的事情是:让一组线程达到一个屏障(也可以叫做同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
