标签:消息传递 包装 接口 命名 自己 最新 没有 模块 如何
第十章
1. 进程和线程:两种不同的并发模块
(1)进程:正在运行程序的一个实例,拥有自己私有专用的内存空间
·可抽象为虚拟计算机,拥有独立的执行环境和完整的资源
·进程间通常不共享内存,不能访问其他进程的内存和对象,需要特殊机制
·进程通信采用消息传递方式,即标准I/O流,为了实现进程间通信,大多数操作系统都支持“进程间通信(IPC)资源”,如管道和socket
(2)线程:正在运行程序的一个执行路径(一个进程可对应多个线程)
·线程有自己的堆栈和局部变量,但是多个线程共享内存空间
·可抽象为一个虚拟处理器,有时也称为轻量级进程
·线程存在于进程内,与进程中的其他线程共享相同的资源
·采用内存共享机制通信,需要特殊处理才能实现消息传递和私有内存
2. 线程的创建和启动、runnable
(1)所有的线程都需要实现Runnable接口,在run()中写具体实现
(2)创建Thread子类型
(3)提供一个Runnable对象
(4)惯用法:用一个匿名的Runnable启动一个线程,避免创建命名类
3. 时间分片、交错执行、竞争条件
(1)时间分片
·在某时刻,一个运行核心上只有一个线程可以运行
·当线程数多于处理器数量时,并发性通过时间片来模拟,处理器切换处理不同的线程
(2)交错执行:时间片的使用不可预知且非确定,线程可能随时暂停或恢复
(3)竞争条件:程序的正确性(后置条件和不变量的满足)取决于并发计算A和B中事件的相对时间,例如A和B都需要从银行中取走全部存款,A取走时B可能不知道,导致了没有足够的存款,产生竞争
4. 线程的休眠、中断
(1)休眠
·sleep():让当前线程暂停指定时间的执行
·join():确保当前线程能够执行完毕
·wait():释放锁,让线程进入等待,直到调用notify()
(2)中断(interrupt)
·每个线程都有中断状态,初始为false
·若该线程在执行低级可中断阻塞方法,取消阻塞,抛出中断异常,否则设置中断状态为true
·在被中断线程中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情
·当一个线程中断另一个线程时,被中断的线程不一定要立即停止,只需在愿意并且方便的时候停止,为安全地构造可取消活动提供了更大的灵活性
·如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态,中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止
5. 线程安全:无论如何执行,不许调度者做额外的协作,都能满足正确性
(1)Confinement:限制可变变量的共享
·将数据限制在单个线程中,避免线程在可变数据上进行竞争
·局部变量保存在线程栈中,每个调用都有自己的变量副本,如果是对象的引用,则要确保不能引用任何其他线程可访问的对象
·在多线程环境中,取消全局变量,尽量避免使用不安全的静态变量
(2)Immutability:用不可变的共享变量
·解决了因为共享可变数据造成的竞争,可以安全地从多个线程访问final
·这种安全性只适用于变量本身,仍需确保变量指向的对象是不可变的
·如果类型的对象在整个生命周期中表示相同的抽象值,则类型不可变
·若改变对用户不可见,且对应抽象值不变,允许对rep进行更改
·不变性的强定义
- 没有改变数据的操作
- 所有字段均为private和final
- 没有表示泄露
- 表示中的任何可变对象都不能变化
- 不存储传递给构造函数的外部可变对象的引用
- 避免在方法返回值中包含对可变对象的引用
(3)Threadsafe data type:将共享数据封装在线程安全的数据类型中
·如List、Set、Map等数据结构是不安全的,线程安全提供了Connections的类型,可确保方法是原子的(动作的内部操作不会同其他操作交叉,不会产生部分完成的情况),例:private static Map<Integer,Boolean> cache = Collections.synchronizedMap(new HashMap<>());
·统一采用包装类的形式,确保抛弃对底层非线程安全容器类的引用
·迭代器仍然不安全,在迭代collection的时候需获取集合的锁
·原子操作仍然不足以完全避免竞争,如检查集合是否为空的时候,另一个线程可能提前取走了元素
·包装的实现是将所有的实际工作委托给指定的容器,但在容器的基础上添加额外的功能
(4)Synchronization:使用同步来防止线程同时访问变量
·锁是一种实现同步的抽象,某一时刻最多允许一个线程拥有锁
·锁的两种操作
- 获取锁的所有权:如过锁被其他线程拥有,将进入阻塞状态,等待锁释放后再同其他线程竞争
- 释放锁的所有权
·使用锁可以确保锁的所有者始终查看最新的数据
·锁只能确保与其他请求获取相同对象锁的线程互斥访问,若其他线程采用不同的锁,则同步失效
·当线程调用同步方法时,它会自动获取该方法对象的内部锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会释放锁
·同一对象上的同步方法的两次调用不会有交错现象
6. Message passing
(1)各个处理模块间通过信息传递来进行交互
(2)接收方将收到的消息形成队列逐一处理,消息发送者继续发送(异步方式)
(3)并不能消除竞争条件的可能性
(4)选择模型时,withdraw-if-sufficient-funds比单纯的withdraw更合适
7. 死锁
(1)由于使用锁需要线程等待,可能会陷入两个线程正在等待对方,陷入永远阻塞的情况
(2)死锁可能涉及两个以上的模块,线程间的依赖关系环是出现死锁的信号
(3)防止死锁方法
·对需要同时获取的锁进行排序,并确保所有代码按照该顺序获取锁定,例:
·用单个粗粒度的锁监控多个对象实例(但性能损失大)
8. 以注释的形式撰写线程安全策略
(1)需要对安全性进行这种仔细的论证,阐述使用了哪种技术,使用threadsafe data types, or synchronization时,需要论证所有对数据的访问都是具有原子性的
(2)例:
(3)反例:
标签:消息传递 包装 接口 命名 自己 最新 没有 模块 如何
原文地址:https://www.cnblogs.com/zhangyushuqing/p/9192493.html