chapter21 Java多线程编程在Netty中的应用
1. Java内存模型与多线程编程
1.1 Java内存模型
- JVM规范定义了Java内存模型,来屏蔽各种操作系统、虚拟机实现厂商和硬件的内存访问差异。
- 工作内存和主内存
- 所有变量都存储在主内存中,每个线程都有自己独立的工作内存,它保存了该线程使用的变量的主内存的复制。线程间的变量访问需要通过主内存来完成。
- Java内存交互协议
- 定义了8种操作来完成主内存和工作内存的变量访问
- lock: 主内存变量,把一个变量标识为某个线程独占的状态。
- unlock:主内存变量,把一个处于锁定的变量释放出来
- read:主内存变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load操作使用
- load:工作内存变量,把read读取到的主内存中的变量值放入到工作内存的变量副本中
- use:工作内存变量,把工作内存变量的值传递给java虚拟机执行引擎,每当虚拟机遇到一个需要使用到变量值的字节码指令时,将会执行该操作
- assign:工作内存变量,把从执行引擎接收到的变量的值赋给工作变量,每当虚拟机遇到一个变量赋值的字节码指令时,将会执行该操作
- store:工作内存变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write操作使用
- write:主内存变量,把store操作从工作内存中的变量值放入主内存的变量中
- Java的线程
- 目前操作系统实现线程的方式主要有三种:【sun的jdk,在windows和linux上采用内核线程的实现方式】
- 内核线程(KLT)实现:有内核来完成线程切换,并负责将线程任务映射到不同的处理器上
- 用户线程实现(UT):用户线程是指完全建立在用户空间线程库上的线程,用户线程的创建、启动、运行、销毁和切换完全在用户态中完成,不需要内核帮助,因此性能更高
- 混合实现:将内核线程和用户线程混合在一起使用的方式
- 工作内存和主内存
2、Netty的并发编程实线
2.1 对共享的可变数据进行正确同步
2.2 正确使用锁
- wait方法使线程等待某个条件,它必须在同步块内部被调用,这个同步块通常会锁定当前对象实例。
- 始终使用wait循环来调用wait方法,永远不要在循环之外调用wait方法。
- 唤醒线程,保守使用notifyAll,当处于等待的所有线程都在等待同一个条件,而每次只有一个线程可以从这个条件中被唤醒,那么就用notify
2.3 volatile的正确使用
- volatile时java提供的最轻量级的同步机制,Java内存模型对volatile专门定了一个特殊的访问规则,仅仅解决了可见性的问题
- 线程可见性
- 禁止指令重排序优化
- volatile最适合使用的是一个线程写,其他线程读的场合。
2.4 CAS指令和原子类
2.5 线程安全类的应用
- 线程池
- 并发集合
- 新的同步器
- 新的原子包装类
2.6 读写锁的应用
- ReadWriteLock使用场景:
- 读多写少
- 读写锁是可重入、可降级的,一个线程获取读写锁后,可以继续递归获取;从写锁可以降级为读锁,以便快速释放锁资源
- ReentrantReadWriteLock支持获取锁的公平策略
- 读写锁支持非阻塞的尝试获取锁,如果获取失败,直接返回false,而不是阻塞
- 获取锁之后一定要释放锁,否则会发生锁溢出异常。
2.7 线程安全性文档说明
2.8 不要依赖线程优先级
- Netty默认的线程工厂提供了实现类,开发了包含设置线程优先级字段的构造函数,这是个错误的决定。实际上JDK的优先级是无法跨平台正确运行的。