文件名称:Java并发编程(学习笔记).xmind
文件大小:990KB
文件格式:XMIND
更新时间:2023-07-10 09:41:03
java 并发 并发编程
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)确保某个计算在其需要的所有资源都被初始化后才能继续执行
(2)确保某个服务在其所依赖的所有其他服务都已经启动之后才启动
(3)等待知道某个操作的所有参与者都就绪再继续执行
CountDownLatch:可以使一个或多个线程等待一组事件发生
FutureTask
*应用场景
(1)用作异步任务使用,且可以使用get方法获取任务的结果
(2)用于表示一些时间较长的计算
状态
等待运行
正在运行
运行完成
使用Callable对象实例化FutureTask类
信号量(Semaphore)
用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量
管理者一组虚拟的许可。acquire获得许可(相当于P操作),release释放许可(相当于V操作)
应用场景
(1)二值信号量可用作互斥体(mutex)
(2)实现资源池,例如数据库连接池
(3)使用信号量将任何一种容器变成有界阻塞容器
栅栏
能够阻塞一组线程直到某个事件发生
栅栏和闭锁的区别
所有线程必须同时到达栅栏位置,才能继续执行
闭锁用于等待事件,而栅栏用于等待线程
栅栏可以重用
形式
CyclicBarrier
可以让一定数量的参与线程反复地在栅栏位置汇集
应用场景在并行迭代算法中非常有用
Exchanger
这是一种两方栅栏,各方在栅栏位置上交换数据。
应用场景:当两方执行不对称的操作(读和取)
线程池
任务与执行策略之间的隐形耦合
线程饥饿死锁
运行时间较长的任务
设置线程池的大小
配置ThreadPoolExecutor
构造参数
corePoolSize
核心线程数大小,当线程数