Changes for page Concurrency

Last modified by chrisby on 2024/06/02 15:15

From version 1.2
edited by chrisby
on 2023/11/26 19:12
Change comment: There is no comment for this version
To version 1.8
edited by chrisby
on 2023/11/26 20:59
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -1,76 +1,88 @@
1 -* When I/O is the bottleneck of your application, more threads will increase the performance in opposite to when CPU is the bottleneck.
2 -* Stress Testing: Checking the throughput of an application by sending a huge amount of requests and examining the response times.
3 -* Isolate concurrent code by putting it in a few separate classes.
4 -* Always consider the concept of execution paths: The amount of possible interleaving of instructions that are processed by at least two threads. For example, objects with mutable states could unintentionally cause different results doing the same operation twice.
5 -* Atomic operation = operation which can not be interrupted by other threads. But for unsynchronized processes threads can put instructions between two atomic operations.
6 -* `synchronized` prevents unintended side-effects.
7 -* Server-based locking is preferred over client-based locking.
8 - * Server-based locking: The class used takes care of internal locking, so the user has nothing else to worry about.
9 - * Client-based locking: User has to manually implement locking. This approach error prone and hard to maintain.
10 -* If there is no access to the server an adapter class can be used instead. Even better would be thread-save collections using extended interfaces.
11 -* As little synchronized code (`synchronized`) as possible should be used. And if, then only for small, critical code sections.
1 +## Concurrency
12 12  
13 -### Prevent Deadlocks
3 +* Objects are abstractions of processing, threads are abstractions of timing.
14 14  
15 -* Do this by making one of its four conditions impossible.
5 +### Why concurrency?
16 16  
17 -### Mutual Exclusion (Mutex)
7 +* Concurrency is a decoupling strategy. The what is decoupled from the when. Concurrency is important and can improve the throughput and structure of an application. On the other hand, it is hard to write clean concurrent code and it is harder to debug. -> own: And to test, right?
8 +* Concurrency doesn't always improve performance behavior and but it always changes the design of the program. When working with containers, you should know exactly what you are doing.
9 +* Concurrency demands a certain amount of management effort, which degrades performance behavior and requires additional code. Proper concurrency is complex, even for simple problems. Concurrency bugs are usually not reproducible; therefore, they are often written off as one-time occurrences (cosmic rays, glitches, etc.) rather than treated as true defects, as they should be. Concurrency requires a fundamental change in design strategy.
10 +* Challenges: When threads access out-of-sync data, incorrect results may be returned.
18 18  
19 -* Description:
20 - * When resources can't be used by mutual thread and
21 - * there are less resources than threads.
22 -* Solutions:
23 - * Use concurrently accessible resources like AtomicInteger.
24 - * Increase the number of resources until its greater or equal to the number of competing threads.
25 - * Check if every required resource is accessible before the task starts.
12 +### Principles of defensive concurrency programming
26 26  
27 -### Lock & Wait
14 +* SRP
15 + * Changes to concurrent code should not be mixed with changes to the rest of the code. So you should separate the two cleanly.
16 + * Concurrency has its own life cycle with development, modification, and polish.
17 + * Concurrent code is associated with special problems that have a different form and often a higher degree of difficulty than non-concurrent code.
18 +* Constrain the range of validity of data
19 + * Take data encapsulation to heart; restrict access to all shared resources.
20 + * So you should keep the mass of shared code low, and shared resources should only be claimed by threads that need them. That means one should divide the blocks and resources into smaller blocks if necessary.
21 +* Work with copies of the data
22 + * One can sometimes get around data sharing by:
23 + * working with copies of objects and treating them as read-only objects.
24 + * making multiple copies of an object, having multiple threads compute results on it, and merging those results into a single thread.
25 + * It is often worth creating multiple objects to avoid concurrency issues.
26 +* Threads should be as independent of each other as possible.
27 + * Threads should not share data or know anything about each other. Instead, they should prefer to work with their own local variables.
28 + * Try to decompose data into independent subsets that can be processed by independent threads, possibly in different processes.
28 28  
29 -* Description:
30 - * Once a thread acquires a resource, it will not release the resource until it has acquired all of the other resources it requires and has completed its work.
31 -* Solutions:
32 - * Before reservation of a resource, check its accessibility.
33 - * If a resource is not accessible, release every resource and start from anew.
34 -* Dangers:
35 - * Starvation: A thread never achieves to reserve all required resources.
36 - * Livelock: Thread gets tangled up.→ This approach is always applicable but inefficient as it causes a bad performance.
30 +### Things to learn before working with concurrency
37 37  
38 -### No preemption
32 +* Get to know your library
33 + * Use the thread-safe collections provided.
34 + * Use the executor framework to execute disjointed tasks.
35 + * Use non-blocking solutions if possible.
36 + * Multiple library classes are not thread-safe.
37 +* Thread-safe collections
38 + * So you should use ConcurrentHashMap instead of HashMap.
39 + * Author's recommendations: java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.
40 +* Get to know execution models
41 + * Basic definitions
42 + * Bound Resources
43 + * Mutual Exclusion
44 + * Starvation
45 + * Deadlock
46 + * Livelock
47 + * Thread Pools
48 + * Future
49 + * Synchronization: General term for techniques that control the access of multiple threads to shared resources.
50 + * Race Condition: A situation where the system's behavior depends on the relative timing of events, often leading to bugs.
51 + * Semaphore: An abstract data type used to control access to a common resource by multiple threads.
52 + * Locks: Mechanisms to ensure that only one thread can access a resource at a time.
53 + * Atomic Operations: Operations that are completed in a single step relative to other threads.
54 + * Thread Safety
55 + * Producer-consumer
56 + * Reader-recorder vs Reader Writer??
57 + * Philosopher problem → Study algorithms and their application in solutions.
39 39  
40 -* Description:
41 - * A thread is unable to steal a resources reserved by another thread.
42 -* Solution:
43 - * A thread is allowed to ask another thread to release all of its resources (including the required one) and starting from anew. This approach is similar to the 'Lock & Wait' solution but has a better performance.
59 +### Watch out for dependencies between synchronized methods
44 44  
45 -### Circular Waiting / Deadly Embrace
61 +* Dependencies between synchronized methods in concurrent code cause subtle bugs.
62 +* Avoid applying more than one method to a shared object. If this is not possible, you have three options:
63 + * Client-based locking: the client should lock the server before the first method is called and ensure that the lock includes the code that calls the last method.
64 + * Server-based locking: Create a method in the server that locks the server, calls all methods, and then unlocks the server. Have the client call the new method.
65 + * Adapted Server: Create an intermediate component that performs the lock. This is a variant of server-based locking if the original server cannot be changed.
66 +* Keep synchronized sections small.
67 + * Locks are expensive because they add administrative overhead to delays. On the other hand, critical sections must be protected.
68 + * Critical sections, are parts of the code that are only executed correctly if several threads do not access it at the same time.
69 + * Keep synchronized sections as small as possible.
46 46  
47 -* Description:
48 - * When two or more threads require a resource which is already reserved by another of these threads.
49 - * Example:
50 - * Thread T1 has resource R1 and waits for R2 to be released.
51 - * Thread T2 has resource R2 and waits for R1 to be released.
52 -* Solution:
53 - * All threads reserve all resources in a the same order.
54 -* Problems:
55 - * The order of reservation doesn't necessarily have to be the same as the order of usage. This leads to inefficiencies like reserving a resource at the beginning which is just required at the end of the task.
56 - * Unnecessarily long locked resources.
57 - * Order can not always be specified.
71 +### Writing correct shutdown code is difficult
58 58  
59 -### Problems of testing multi-threaded methods
73 +* You should think about a shutdown as early as possible and get it running as soon as possible. If you wait, it will always take longer. Study the available algorithms because this task is probably harder than you think.
74 + * TODO: Why do I need shutdown code?
60 60  
61 -* Very tricky which is why concurrency should be avoided in the first place.
62 -* In general this requires a lot of iteration which makes it resource intensive.
63 -* The outcome is architecture dependent (OS, hardware) which introduced randomness and make the error detection unreliable.
76 +Some Notes:
64 64  
65 -### Solution approaches for testing multi-threaded methods
66 -
67 -* Monte-Carlo-Tests
68 - * Write flexible, adaptive tests.
69 - * Repeatedly run them on a test server and randomly vary the test settings.
70 - * If something fails, the code is defect and the applied settings should be logged.
71 - * Do this early to gain tests ASAP for your testing repertoire or CI-server.
72 -* Execute these tests on every platform over a long time to increase the probability that
73 - * the production code is correct or
74 - * the test code is bad.
75 -* Execute the tests on a computer using simulations of application loads when possible.
76 -* There are tools to test thread-based code like ConTest.
78 +* When I/O is the bottleneck of your application, more threads will increase the performance in opposite to when CPU is the bottleneck.
79 +* Stress Testing: Checking the throughput of an application by sending a huge amount of requests and examining the response times.
80 +* Isolate concurrent code by putting it in a few separate classes.
81 +* Always consider the concept of execution paths: The amount of possible interleaving of instructions that are processed by at least two threads. For example, objects with mutable states could unintentionally cause different results doing the same operation twice.
82 +* Atomic operation = operation which can not be interrupted by other threads. But for unsynchronized processes threads can put instructions between two atomic operations.
83 +* `synchronized` prevents unintended side-effects.
84 +* Server-based locking is preferred over client-based locking.
85 + * Server-based locking: The class used takes care of internal locking, so the user has nothing else to worry about.
86 + * Client-based locking: User has to manually implement locking. This approach error prone and hard to maintain.
87 +* If there is no access to the server an adapter class can be used instead. Even better would be thread-save collections using extended interfaces.
88 +* As little synchronized code (`synchronized`) as possible should be used. And if, then only for small, critical code sections.