Java并发编程
背景介绍
并发历史
必要性
进程
资源分配的最小单位
线程
CPU调度的最小单位
线程的优势
(1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率
(2)建模简单:通过使用线程可以讲复杂并且异步的工作流进一步分解成一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置交互
(3)简化异步事件的处理:服务器应用程序在接受来自多个远程客户端的请求时,如果为每个连接都分配一个线程并且使用同步IO,就会降低开发难度
(4)用户界面具备更短的响应时间:现代GUI框架中大都使用一个事件分发线程(类似于中断响应函数)来替代主事件循环,当用户界面用有事件发生时,在事件线程中将调用对应的事件处理函数(类似于中断处理函数)
线程的风险
线程安全性:永远不发生糟糕的事情
活跃性问题:某件正确的事情迟早会发生
问题:希望正确的事情尽快发生
服务时间过长
响应不灵敏
吞吐率过低
资源消耗过高
可伸缩性较低
线程的应用场景
Timer
确保TimerTask访问的对象本身是线程安全的
Servlet和JSP
Servlet本身要是线程安全的
正确协同一个Servlet访问多个Servlet共享的信息
远程方法调用(RMI)
正确协同多个对象中的共享状态
正确协同远程对象本身状态的访问
Swing和AWT
事件处理器与访问共享状态的其他代码都要采取线程安全的方式实现
框架通过在框架线程中调用应用程序代码将并发性引入应用程序,因此对线程安全的需求在整个应用程序中都需要考虑
基础知识
线程安全性
定义
当多个线程访问某个类时,这个类始终能表现出正确的行为,那么就称这个类是线程安全的
无状态对象一定是线程安全的,大多数Servlet都是无状态的
原子性
一组不可分割的操作
竞态条件
基于一种可能失效的观察结果来做出判断或执行某个计算
复合操作:执行复合操作期间,要持有锁
锁的作用
加锁机制、用锁保护状态、实现共享访问
锁的不恰当使用可能会引起程序性能下降
对象的共享使用策略
线程封闭:线程封闭的对象只能由一个线程拥有并修改
Ad-hoc线程封闭
栈封闭
ThreadLocal类
只读共享:不变对象一定是线程安全的
尽量将域声明为final类型,除非它们必须是可变的
分类
不可变对象
事实不可变对象
线程安全共享
封装有助于管理复杂度
线程安全的对象在其内部实现同步,因此多个接口可以通过公有接口来进行访问
保护对象:被保护的对象只能通过特定的锁来访问
将对象封装到线程安全对象中
由特定锁保护
保护对象的方法
对象的组合
设计线程安全的类
实例封闭
线程安全的委托
委托是创建线程安全类的最有效策略,只需要让现有的线程安全类管理所有的状态
在现有线程安全类中添加功能
将同步策略文档化
基础构建模块
同步容器类
分类
Vector
Hashtable
实现线程安全的方式
将状态封装起来,对每个公有方法都进行同步
存在的问题
复合操作
修正方式
客户端加锁
迭代器
并发容器
ConcurrentHashMap
用于替代同步且基于散列的Map
CopyOnWriteArrayList
用于在遍历操作为主要操作的情况下替代同步的List
Queue
ConcurrentLinkedQueue
*BlockingQueue
提供了可阻塞的put和take方法
生产者-消费者模式
中断的处理策略
传递InterruptedException
恢复中断,让更高层的代码处理
PriorityQueue(非并发)
ConcurrentSkipListMap
替代同步的SortedMap
ConcurrentSkipListSet
替代同步的SortedSet
Java 5
Java 6
同步工具类
闭锁
*应用场景
(1)
1