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域,编译器和处理器要遵守两个重排序规则。

  1. 对于构造函数内对一个final域的写入,与随后把这个构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
  2. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能排序。

6.3 Java中的阻塞队列

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加操作支持阻塞的插入和移除方法。

  1. 支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的过程,知道队列不满
  2. 支持阻塞的移除方法:当队列为空时,获取元素的线程会等待队列变为非空。
方法/处理方式 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 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运行一个或多个线程等待其他线程完成操作。

  1. import java.util.concurrent.CountDownLatch;
  2. public class CountDownLatchTest {
  3. static CountDownLatch c = new CountDownLatch(2);
  4. public static void main(String[] args) throws InterruptedException {
  5. new Thread(() -> {
  6. System.out.println(1);
  7. c.countDown();
  8. System.out.println(2);
  9. c.countDown();
  10. }).start();
  11. //不要写成 wait()
  12. c.await();
  13. System.out.println(3);
  14. }
  15. }

8.2 同步屏障CyclicBarrier

它要做的事情是:让一组线程达到一个屏障(也可以叫做同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。