【软件构造】课程提纲(7)

第十章

1. 进程和线程:两种不同的并发模块

(1)进程:正在运行程序的一个实例,拥有自己私有专用的内存空间

  ·可抽象为虚拟计算机,拥有独立的执行环境和完整的资源

  ·进程间通常不共享内存,不能访问其他进程的内存和对象,需要特殊机制

  ·进程通信采用消息传递方式,即标准I/O流,为了实现进程间通信,大多数操作系统都支持“进程间通信(IPC)资源”,如管道和socket

(2)线程:正在运行程序的一个执行路径(一个进程可对应多个线程)

  ·线程有自己的堆栈和局部变量,但是多个线程共享内存空间

  ·可抽象为一个虚拟处理器,有时也称为轻量级进程

  ·线程存在于进程内,与进程中的其他线程共享相同的资源

  ·采用内存共享机制通信,需要特殊处理才能实现消息传递和私有内存

扫描二维码关注公众号,回复: 1614343 查看本文章

2. 线程的创建和启动、runnable

(1)所有的线程都需要实现Runnable接口,在run()中写具体实现

   

2创建Thread子类型

   

3)提供一个Runnable对象

   

4)惯用法:用一个匿名的Runnable启动一个线程,避免创建命名类

   

3. 时间分片、交错执行、竞争条件

(1)时间分片

  ·在某时刻,一个运行核心上只有一个线程可以运行

  ·当线程数多于处理器数量时,并发性通过时间片来模拟,处理器切换处理不同的线程

(2)交错执行:时间片的使用不可预知且非确定,线程可能随时暂停或恢复

 

(3)竞争条件:程序的正确性(后置条件和不变量的满足)取决于并发计算AB中事件的相对时间,例如AB都需要从银行中取走全部存款,A取走时B可能不知道,导致了没有足够的存款,产生竞争

4. 线程的休眠、中断

(1)休眠

  ·sleep():让当前线程暂停指定时间的执行

  ·join():确保当前线程能够执行完毕

  ·wait():释放锁,让线程进入等待,直到调用notify()

(2)中断(interrupt

  ·每个线程都有中断状态,初始为false

  ·若该线程在执行低级可中断阻塞方法,取消阻塞,抛出中断异常,否则设置中断状态为true

  ·在被中断线程中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情

  ·当一个线程中断另一个线程时,被中断的线程不一定要立即停止,只需在愿意并且方便的时候停止,为安全地构造可取消活动提供了更大的灵活性

  ·如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态,中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止

5. 线程安全:无论如何执行,不许调度者做额外的协作,都能满足正确性

(1)Confinement:限制可变变量的共享

  ·将数据限制在单个线程中,避免线程在可变数据上进行竞争

  ·局部变量保存在线程栈中,每个调用都有自己的变量副本,如果是对象的引用,则要确保不能引用任何其他线程可访问的对象

  ·在多线程环境中,取消全局变量,尽量避免使用不安全的静态变量

(2)Immutability:用不可变的共享变量

  ·解决了因为共享可变数据造成的竞争,可以安全地从多个线程访问final

  ·这种安全性只适用于变量本身,仍需确保变量指向的对象是不可变的

  ·如果类型的对象在整个生命周期中表示相同的抽象值,则类型不可变

  ·若改变对用户不可见,且对应抽象值不变,允许对rep进行更改

  ·不变性的强定义

    - 没有改变数据的操作

    - 所有字段均为privatefinal

    - 没有表示泄露

    - 表示中的任何可变对象都不能变化

    - 不存储传递给构造函数的外部可变对象的引用

    - 避免在方法返回值中包含对可变对象的引用

(3)Threadsafe data type:将共享数据封装在线程安全的数据类型中

  ·如ListSetMap等数据结构是不安全的,线程安全提供了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)反例:

   

猜你喜欢

转载自www.cnblogs.com/zhangyushuqing/p/9192493.html
今日推荐