《代码整洁之道》笔记——第十三章:并发编程

1、有关编写并发软件的中肯说法:

  • 并发会在性能和编写额外代码上增加一些开销;
  • 正确的并发是复杂的,即便对于简单的问题也是如此;
  • 并发缺陷并非总能重现,所以常被看做偶发事件而忽略,未被当做真的缺陷看待;
  • 并发常常需要对设计策略的根本性修改。

2、防御并发代码问题的原则和技巧

  • 单一权责原则。分离并发相关代码与其他代码。
  • 限制数据作用域。谨记数据封装;严格限制对可能被共享的数据的访问。
  • 使用数据复本。假使使用对象复本能避免代码同步执行,则因避免了锁定而省下的价值有可能补偿得上额外的创建成本和垃圾收集开销。
  • 线程应尽可能地独立。尝试将数据分解到可被独立线程(可能在不同处理器上)操作的独立子集。

3、生产者-消费者模型。一个或多个生产者线程创建某些工作,并置于缓存或队列中。一个或多个消费者线程从队列中获取并完成这些工作。生产者和消费者之间的队列是一种限定资源。

4、学习和生产者-消费者模型,读者-作者模型,演习哲学家有关的算法,大多数情况下遇到的并发问题,都是这三个问题的变种,可以用相关算法解决。

5、避免使用一个共享对象的多个方法。有时必须使用一个共享对象的多个方法。在这种情况发生时,有3种写对代码的手段:

  • 基于客户端的锁定——客户端代码在调用第一个方法前锁定服务端,确保锁的范围覆盖了调用最后一个方法的代码;
  • 基于服务端的锁定——在服务端内创建锁定服务端的方法,调用所有方法,然后解锁。让客户端代码调用新方法;
  • 适配服务端——创建执行锁定的中间层。这是一种基于服务端的锁定的例子,但不修改原始服务端代码。

6、保持同步区域微小。锁是昂贵的,因为它们带来了延迟和额外开销。另一方面,临界区应该被保护起来。所以,应该尽可能少地设计临界区。

7、很难编写正确的关闭代码。建议:尽早考虑关闭问题,尽早令其工作正常。这会花费比预期更多的时间。检视既有算法,因为这可能会比想象中难得多。

8、测试线程代码。编写有潜力曝露问题的测试,在不同的编程配置、系统配置和负载条件下频繁运行。如果测试失败,跟踪错误。别因为后来测试通过了后来的运行就忽略失败。下面是一些精练的建议:

  • 将伪失败看作可能的线程问题。建议:不要将系统错误归咎于偶发事件。
  • 先使非线程代码可工作。建议:不要同时追踪非线程缺陷和线程缺陷。确保代码在线程之外可工作。
  • 编写可插拔的线程代码。建议:编写可插拔的线程代码,这样就能在不同的配置环境下运行。
  • 编写可调整的线程代码。要允许线程数量可调整。在系统运行时允许线程发生变动。允许线程依据吞吐量和系统使用率自我调整。
  • 运行多于处理器数量的线程;
  • 在不同平台上运行;
  • 调整代码并强迫错误发生。可以通过装置代码,增加对Object.wait()、Object.sleep()、Object.yield()和Object.priority()等方法的调用,改变代码执行顺序来增加捕捉到线程错误的发生。

9、装置代码的两种方法:硬编码、自动化。

10、硬编码:手动在代码各处插入Object.wait()、Object.sleep()、Object.yield()和Object.priority()等方法的调用。这种手法有许多毛病:

  • 你得手工找到合适的地方来插入方法调用;
  • 你怎么知道在哪里插入调用、插入什么调用?
  • 不必要地在产品环境中留下这类代码,将拖慢代码执行速度;
  • 这是种无的放矢的手段。你可能找不到缺陷。实际上,这不在你把握之中。

11、自动化:可以使用Aspect-Oriented Framework、CGLIB或ASM之类工具通过编程来装置代码。在一个方法中随机选取要调用的方法,并在代码的不同位置调用这个方法。建议:使用异动策略搜出错误。

posted @ 2020-03-06 23:57  夜里寻星  阅读(159)  评论(0编辑  收藏  举报