{Clean Code} Concurrency

"Objects are abstractions of processing. Threads are abstractions of schedule."  


Why Concurrency?

Decoupling what from when can improve both the throughput and structures of an application. This makes the system easier to understand and offers some powerful ways to separate concerns.


Myths and Misconceptions

1. Concurrency always improves performance.

False, sometimes.

2. Design does not change when writing concurrent programs

No, the design of a concurrent algorithm is different.

3. Understanding concurrency issues is not important when working with a container such as a Web or EJB container.

You'd better know about your resources.


More balanced sound bites

Concurrency incurs some overhead, both in performance as well as writing additional code.

Correct concurrency is complex, even for simple problems.

Concurrency bugs are not usually repeatable, so they are often ignored as one-offs.

Concurrency often requires a fundamental change in desgin strategy.


Challeges

Understant what Just-In-Time Compiler does with generated byte-code, and understand what the Java memory model considers to be atomic.

Possible execution paths are too huge.


Concurrency Defense Principles

SRP

Concurrency design is complex and deserves to be separated from the rest of the code.

Concurrency-related code has its own life cycle of development, change and tuning.

Concurrency-related code has its own challenges, which are different from and more difficult than nonconcurrency-related code.

Recommendation: Keep your Concurrency-related code separate from other code.


Corollary: Limit the Scope of Data

Use synchronized word to protect a critical section in the code that uses the shared object.

Recommendation: Take data encapsulation to heart, severely limit the access of any data that may be shared.


Corollary: Use Copies of Data

If using copies of objects allows the code to avoid synchronizing, the savings in avoiding the intrinsic lock will likely make up for the additional creation and garbage collection overhead.


Corollary: Thread Should Be as Independent as Possible

Recommendation: Attempt to partition data into independent subsets than can be operated on by independent threads, possibly in different processors.


Know Your Library

After 1.5, use the thread-safe collections, executor framework for executing unrelated tasks, nonblocking solutions when possible, and notice several library classes are not thread-safe.


java.util.concurrent

ConcurrentHashMap is better than HashMap in nearly all situations.

ReentrantLock: a lock that can be acquired in one method and released in another.

Semaphore: an implementation of the classic semaphore, a lock with a count.

CountDownLatch: a lock that waits for a number of events before releasing all threads waiting on it. This allows all threads to have a fair chance of starting at about the same time.

Recommendation: Review the classes avaiable to you. Be familiar with java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.


Know Your Execution Models

Basic concepts:



Producer-Consumer: 

a consumer can read only when the queue is not empty, a producer can write only when the queue is not full.

Readers-Writers:

Balance the needs for both readers and writers to satisfy correct operation, provide reasonable throughput and avoiding starvation. 

Make writers wait until there are no readers, but need to notice the starvation and throughput problems.

Dining Philosophers:

Compete resources.

Recommendation: Learn these basic algorithms and understand their solutions.


Beware Dependencies Between Synchronized Methods

Recommendation: Avoid using more than one method on a shared object.

Client-Based Locking: 

Have the client lock the server before calling the first method and make sure the lock's extent includes code calling the last method.

Server-Based Locking:

Within the server, create a method that locks the server, calls all the methods, and then unlocks. Have the client call the new method.

Adapted Server:

Create an intermediary that performs the locking. This is an example of server-based locking, where the original server cannot be changed.


Keep Synchronized Sections Small

Locks are expensive because they increase the overhead and delays.


Writing Correct Shut-down Code Is Hard

Shutdown gracefully is hard.

Recommendation: Think about shut-down early and get it working early. Review existing codes because this is probably harder than you think.


Testing Threaded Code

Recommendation: 

Write tests that have the potential to expose problems and then run them frequently with differentprogrammatic configurations and system configurations and load. If tests ever fail, track down the failure. Don't ignore a failure just because the tests pass on a subsequent run.


Treat spurious failures as candidate threading issues.

  • Get your nonthreaded code working first.

  Do not try to chase down nonthreading bugs and threading bugs at the same time.

  • Make your threaded code pluggable.

Enable variable configurations.

  • Make your threaded code tunable.
  • Run with more threads than processors.
  • Run on different platforms.
  • Instrument your code to try and force failures.

Adding Object.wait/sleep/yield/priority to force failing pathway emerge.

Add jiggle methods to ferret out errors. ConTest --- from IBM.



Conclusion:

Know the possible reasons: multiple threads operating on shared data, using a common resource pool, boundary cases, shutting-down.

Learn your library so you can use the features to solve your problems.

Keep the amount of shared objects as narrow as possible.


Appendix:

Client/Server Example

Where the time is spent:

1. I/O : 

using a socket, connecting to a database, waiting for virtual memory swapping ..

2. Processor:

numerical calculations, regular expression processing, garbage collection ...

 

If the program is I/O bound, adding threads can help improve the performance.

1. consider heavy cilents 

2. consider server's responsibility (consider abstraction level and responsibilities)

E.g 



We can create ClientScheduler interface, and have different implementations.


Possible Paths of Execution

For N instructions in a sequence, no looping or conditionals and T threads, the total number of possible execution paths is equal to 

( ( N * T ) ! ) / ( ( N ! ) ^ T )

Assignment to any 64-bit value requires 2 32-bit assignments, so set long is not atomic.


Know your library

Executor, Nonblocking solutions (AtomicBoolean/Integer, take advantage of modern processors' Compare and Swap operation.), Nonthread-safe classes (SimpleDateFormat, DataBase connections, containers in java.util --- hashtable, putIfAbsent(), servlets)


Dependencies Between Methods Can Break Concurrent Code

Use server-based locking:

It reduces repeated code in clients.

It allows for better performance, easy to replace server.

It reduces the possibility of error.

It enforces a single policy.

It reduces the scope of the shared variables.

If you don't have access to server's codes, use Adapter pattern.


Deadlock:

4 conditions: mutual exclusion (resources cannot be used by multithreads at same time, or limited), lock and wait, no preemption (cannot get other's resources), circular wait.






In this chapter we talked about concurrent update, and the disciplines of clean synchronization and locking that can prevent it. We talked about how threads can enhance the throughput of an I/O-bound system and showed the clean techniques for achieving such improvements. We talked about deadlock and the disciplines for preventing it in a clean way. Finally, we talked about strategies for exposing concurrent problems by instrumenting your code.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
工作第一天 看看这个吧~~ 修炼内功必备 呵呵~ 第1章 假想的编译程序 读者可以考虑一下倘若编译程序能够正确地指出代码中的所有问题,那相应程序的错误情况会怎样?这不单指语法错误,还包括程序中的任何问题,不管它有多么隐蔽。例如,假定程序中有“差1”错误,编译程序可以采用某种方法将其查出,并给出如下的错误信息 -> line 23: while (i<=j) off by one error: this should be '<' 又如,编译程序可以发现算法中有下面的错误: -> line 42: int itoa(int i, char* str) algorithm error: itoa fails when i is -32768 再如,当出现了参数传递错误时,编译程序可以给出如下的错误信息: -> line 318: strCopy = memcpy(malloc(length), str, length); Invalid argument: memcpy fails when malloc returns NULL 好了,要求编译程序能够做到这一程度似乎有点过分。但如编译程序真能做到这些,可以想象编写无错程序会变得多么容易。那简直是小事一桩,和当前程序员的一般作法真没法比。 假如在间谍卫星上用摄像机对准某个典型的软件车间.就会看到程序员们正弓着身子趴在键盘上跟踪错误;旁边,测试者正在对刚作出的内部版本发起攻击,轮番轰炸式地输入人量的数据以求找出新的错误。你还会发现,测试员正在检查老版本的错误是否溜进了新版本。可以推想,这种查错方法比用上面的假想编译程序进行查错要花费大得多的工作量、确实如此,而且它还要有点运气。 运气?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anyanyamy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值