Skip to content

Commit 32ad5e0

Browse files
committed
WIP
1 parent ce7bf5a commit 32ad5e0

25 files changed

+787
-430
lines changed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,21 @@
2222

2323
### Metrics
2424
```text
25-
14883 number of properties
26-
10262 number of functions
27-
8797 number of classes
25+
14885 number of properties
26+
10265 number of functions
27+
8799 number of classes
2828
227 number of packages
29-
3461 number of kt files
29+
3462 number of kt files
3030
```
3131

3232

3333
### Complexity Report
3434
```text
35-
257847 lines of code (loc)
36-
157813 source lines of code (sloc)
37-
115157 logical lines of code (lloc)
38-
72253 comment lines of code (cloc)
39-
24539 cyclomatic complexity (mcc)
35+
257896 lines of code (loc)
36+
157843 source lines of code (sloc)
37+
115174 logical lines of code (lloc)
38+
72260 comment lines of code (cloc)
39+
24542 cyclomatic complexity (mcc)
4040
20171 cognitive complexity
4141
0 number of total code smells
4242
45 comment source ratio
Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,64 @@
1-
# Atomic
1+
# Atomic Operations in Java and Kotlin
2+
---
23

3-
In the Java Virtual Machine (JVM), the term "atomic" refers to operations that are performed as a single, indivisible
4-
step. This means that the operation is completed without any interference or observation from other threads.
5-
Atomicity is a crucial concept in concurrent programming, ensuring that certain actions happen entirely or not at all,
6-
preventing race conditions and data corruption.
4+
<div align="center">
5+
<img src="img/concurrency/atomic.svg" alt="Figure 1" width="700"/>
6+
<p style="text-align:center;">Figure 2</p>
7+
</div>
78

8-
## Atomic Operations
9+
---
910

10-
1. **Primitive Types**:
11-
- **Read and write operations** on primitive types like `int`, `short`, `byte`, `char`, `float`, and `boolean` are
12-
atomic, except for `long` and `double` on some architectures. On 64-bit architectures, `long` and `double` are
13-
typically atomic, but on 32-bit architectures, they might not be.
11+
Atomic operations are fundamental building blocks in concurrent programming that ensure thread-safe updates to shared variables. Let's explore how they work and why they're essential for reliable multi-threaded applications.
1412

15-
2. **Volatile Variables**:
16-
- Declaring a variable `volatile` ensures that reads and writes to that variable are atomic. Additionally, it ensures
17-
visibility, meaning changes to a volatile variable by one thread are immediately visible to other threads.
13+
## What Makes an Operation Atomic?
1814

19-
3. **Atomic Classes in `java.util.concurrent.atomic`**:
20-
- Java provides classes in the `java.util.concurrent.atomic` package for performing atomic operations on variables.
21-
These classes include:
22-
- `AtomicInteger`
23-
- `AtomicLong`
24-
- `AtomicBoolean`
25-
- `AtomicReference`
26-
- These classes provide methods like `get()`, `set()`, `incrementAndGet()`, `compareAndSet()`, etc., which are
27-
implemented using low-level atomic instructions provided by the hardware, ensuring atomicity.
15+
An atomic operation is one that executes as a single, uninterruptible unit. Just like a transaction in a database, either the entire operation completes successfully, or nothing changes at all.
2816

29-
### Why Atomicity is Important
17+
## Primitive Types
3018

31-
Atomicity is essential in multi-threaded environments to ensure that operations are completed without interference.
32-
Without atomic operations, multiple threads could read and write shared data simultaneously, leading to inconsistent and
33-
unpredictable results.
19+
✅ Basic types (`int`, `short`, `byte`, `char`, `float`, and `boolean`) have atomic read/write operations
20+
`long` and `double` require special handling:
21+
⚠️ Not atomic on 32-bit systems
22+
✅ Usually atomic on 64-bit systems
23+
💡 Use `volatile` keyword to ensure atomicity
3424

35-
### Example: Using `AtomicInteger`
25+
## Volatile Variables
3626

37-
Here's an example of using `AtomicInteger` to perform atomic operations:
27+
🔄 Ensures immediate visibility across threads
28+
📝 Guarantees atomic read/write operations
29+
🔄 Prevents compiler optimizations that could break thread safety
30+
31+
## Atomic Classes
32+
33+
```java
34+
AtomicInteger // For integers
35+
AtomicLong // For long values
36+
AtomicBoolean // For boolean flags
37+
AtomicReference // For object references
38+
```
39+
40+
Common atomic methods:
41+
🔄 `get()` - Atomic read operation
42+
`incrementAndGet()` - Atomically increment and retrieve
43+
↔️ `compareAndSet()` - Compare value and atomically set new value if match
44+
45+
## Hardware Support
46+
47+
Modern processors provide specialized instructions for atomic operations:
48+
49+
### Compare-and-Swap (CAS)
50+
51+
💪 Compares a memory location's value with expected value
52+
💪 Only updates if values match
53+
💪 Executes as a single, uninterruptible operation
54+
55+
### Load-Link/Store-Conditional
56+
57+
🔗 Load-link reads value and marks memory location
58+
🔗 Store-conditional writes only if no other thread modified the location
59+
🔗 Ensures atomicity through hardware-level locking
60+
61+
## Practical Example
3862

3963
```kotlin
4064
import java.util.concurrent.atomic.AtomicInteger
@@ -44,6 +68,7 @@ object AtomicExample {
4468

4569
@JvmStatic
4670
fun main(args: Array<String>) {
71+
// Create two threads performing 1000 increments each
4772
val task = Runnable {
4873
for (i in 0 until 1000) {
4974
counter.incrementAndGet()
@@ -53,33 +78,32 @@ object AtomicExample {
5378
val t1 = Thread(task)
5479
val t2 = Thread(task)
5580

81+
// Start threads
5682
t1.start()
5783
t2.start()
5884

59-
try {
60-
t1.join()
61-
t2.join()
62-
} catch (e: InterruptedException) {
63-
e.printStackTrace()
64-
}
85+
// Wait for completion
86+
t1.join()
87+
t2.join()
6588

66-
println("Counter: ${counter.get()}")
89+
// Should print 2000 (1000 increments × 2 threads)
90+
println("Final counter value: ${counter.get()}")
6791
}
6892
}
6993
```
7094

71-
Atomic operations in the JVM are implemented using low-level mechanisms provided by the hardware and the operating
72-
system. These mechanisms ensure that atomic operations are performed without interference from other threads. Here is a
73-
detailed look at how atomic operations work under the hood:
95+
## Best Practices
96+
97+
### Use atomic operations when:
98+
99+
🔒 Updating shared variables in multi-threaded environments
100+
🔒 Implementing thread-safe counters or flags
101+
🔒 Building lock-free data structures
74102

75-
Hardware Support
76-
Modern processors provide specific instructions that ensure atomicity. These instructions are part of the CPU's
77-
instruction set and are used to perform atomic read-modify-write operations. Examples of such instructions include:
103+
### Avoid atomic operations when:
78104

79-
* Compare-and-Swap (CAS): This instruction compares the value at a memory location with an expected value and, only if
80-
they match, modifies the memory location to a new value. This is done atomically, ensuring no other thread can interfere
81-
during the operation.
105+
⚠️ Working with thread-local variables
106+
⚠️ Performance is critical and synchronization isn't needed
107+
⚠️ Using synchronized blocks would be more readable
82108

83-
* Load-Link/Store-Conditional (LL/SC): These instructions work together to provide atomicity. The load-link reads the
84-
value, and the store-conditional writes the value only if no other write has occurred to the memory location since the
85-
load-link.
109+
Remember that atomic operations provide a powerful tool for building thread-safe applications, but they shouldn't replace proper synchronization entirely. Choose the right concurrency primitive based on your specific requirements and performance constraints.
Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,104 @@
1-
# Happens-Before Relationship
1+
# 🔄 Happens-Before Relationship in Concurrency
22

3-
In concurrency, the **happens-before** relationship is a fundamental concept used to ensure that operations are
4-
performed in a predictable and safe order. It helps define the ordering constraints between different operations in a
5-
concurrent program, ensuring that certain operations are completed before others begin. Here’s a breakdown of the
6-
concept:
3+
## 📌 Overview
4+
In concurrency, the **happens-before** relationship is a fundamental concept used to ensure that operations are **performed in a predictable and safe order**. It defines ordering constraints between different operations in a concurrent program, ensuring that certain operations are **completed before others begin**.
75

8-
## 1. Definition
6+
---
97

10-
The happens-before relationship is a partial ordering of operations in a concurrent system that guarantees that if one
11-
operation A happens-before another operation B, then the effects of A are visible to B. In other words, if operation A
12-
happens-before operation B, then B will see the results of A.
8+
## 1️⃣ Definition
9+
The **happens-before** relationship is a **partial ordering** of operations in a concurrent system. It guarantees that:
1310

14-
## 2. Why It Matters
11+
✔ If **operation A happens-before operation B**, then the **effects of A are visible to B**.
12+
✔ If there is **no happens-before relationship**, then **no guarantee** exists regarding the order or visibility of changes.
1513

16-
The happens-before relationship is crucial for understanding and reasoning about concurrency issues such as race
17-
conditions, visibility of changes across threads, and ordering of operations. It helps ensure consistency and
18-
correctness in concurrent programs.
14+
📝 **Example:** If one thread writes a variable, and another thread reads it, the happens-before relationship ensures the read operation sees the updated value.
1915

20-
## 3. Rules and Examples
16+
---
2117

22-
Here are some common rules and examples of happens-before relationships:
18+
## 2️⃣ Why It Matters
19+
The happens-before relationship is crucial for understanding and preventing concurrency issues like:
20+
21+
- **Race conditions** ⚠ (Ensuring that no two threads interfere with each other unpredictably)
22+
- **Visibility of changes** 🔍 (Ensuring that changes made by one thread are visible to another)
23+
- **Ordering of operations** 🔄 (Ensuring that operations execute in the intended sequence)
24+
25+
Without a proper happens-before relationship, one thread **might not see updates** from another, leading to unpredictable behavior.
26+
27+
---
28+
29+
## 3️⃣ Rules and Examples
30+
31+
### 🔹 **Rule 1: Program Order Rule**
32+
Within a **single thread**, statements execute **in the order they appear** in the code.
33+
34+
📝 **Example:**
35+
```java
36+
int x = 5; // Happens-before the next statement
37+
int y = x + 2; // y will correctly see x as 5
38+
39+
### 🔹 Rule 2: Monitor Lock Rule
40+
Unlocking a synchronized block happens-before any subsequent locking of the same monitor.
41+
42+
📝 **Example:**
43+
```java
44+
synchronized (lock) {
45+
sharedValue = 42; // Happens-before another thread locks and reads sharedValue
46+
}
47+
```
48+
49+
### 🔹 Rule 3: Volatile Variable Rule
50+
A write to a volatile variable happens-before any subsequent read of the same variable.
51+
52+
📝 **Example:**
53+
```java
54+
volatile boolean flag = false;
55+
56+
Thread 1:
57+
flag = true; // Happens-before Thread 2 reads it
58+
59+
Thread 2:
60+
if (flag) {
61+
System.out.println("Flag is true!"); // Guaranteed to see updated value
62+
}
63+
```
64+
### 🔹 Rule 4: Thread Start Rule
65+
A call to Thread.start() on a new thread happens-before any actions within that thread.
66+
67+
📝 **Example:**
68+
```java
69+
Thread t = new Thread(() -> System.out.println("Thread started!"));
70+
t.start(); // Happens-before the message is printed
71+
```
72+
73+
### 🔹 Rule 5: Thread Termination Rule
74+
A thread’s termination happens-before another thread detecting it via Thread.join().
75+
76+
📝 **Example:**
77+
78+
```java
79+
Thread t = new Thread(() -> System.out.println("Task complete"));
80+
t.start();
81+
t.join(); // Happens-before main thread continues execution
82+
System.out.println("Thread has finished");
83+
```
84+
85+
### 🔹 Rule 6: Inter-Thread Communication (Executor & Future)
86+
A result written using Future.set() happens-before another thread retrieving the value via Future.get().
87+
88+
📝 **Example:**
89+
```java
90+
ExecutorService executor = Executors.newSingleThreadExecutor();
91+
Future<Integer> future = executor.submit(() -> 100);
92+
93+
System.out.println(future.get()); // Happens-after the computation completes
94+
```
95+
96+
## 🏁 Conclusion
97+
The happens-before relationship provides ordering guarantees in concurrent programs, ensuring thread safety, visibility, and consistency. Key takeaways:
98+
99+
✔ Volatile variables ensure visibility
100+
✔ Synchronization ensures mutual exclusion and ordering
101+
✔ Thread lifecycle methods (start(), join()) establish execution order
102+
✔ Proper concurrency control prevents race conditions
103+
104+
By following these rules, developers can write safe and predictable multi-threaded applications in Java! 🚀
Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,25 @@
1-
Problem: Starvation occurs when a thread is perpetually denied necessary resources to proceed with its execution because
2-
other threads keep consuming those resources.
1+
# 🚀 Starvation in Java: Understanding the Problem and Solutions
2+
3+
## 🔹 What is Starvation?
4+
**Starvation** occurs when a thread is **perpetually denied access** to necessary resources, preventing it from making progress. This typically happens when **higher-priority threads or resource-hogging threads** continuously consume the available CPU or synchronization locks, leaving lower-priority threads **waiting indefinitely**.
5+
6+
---
7+
8+
## 🔥 Problem: Why Does Starvation Occur?
9+
Starvation happens when:
10+
11+
1️⃣ **Thread Priority Imbalance**
12+
- Threads with **higher priority** keep executing, while lower-priority threads get **little to no CPU time**.
13+
- Example: A **high-priority** thread is always scheduled before a **low-priority** thread.
14+
15+
2️⃣ **Unfair Locking (Biased Locking)**
16+
- A thread might **never acquire a lock** if other threads keep **acquiring and releasing** it quickly.
17+
- Example: A thread waiting for a **synchronized** block but never gets a turn because other threads **continuously hold the lock**.
18+
19+
3️⃣ **Unfair Resource Allocation**
20+
- Shared resources are **continuously consumed** by a few threads, **preventing** other threads from using them.
21+
- Example: A **thread pool** where some worker threads **always get tasks**, while others remain idle.
22+
23+
4️⃣ **Frequent Lock Contention**
24+
- If a lock is frequently **acquired and released** by the same few threads, **other threads may never get access**.
25+
- Example: A **database connection pool** where a few connections are always occupied by the same threads.
Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,45 @@
1-
Synchronized in JVM
2-
3-
Synchronized is a keyword in Java that is used to mark a method or a block of code as synchronized. When a method is marked as synchronized, the thread that is executing it is granted a lock on the object the method belongs to. This means that no other thread can execute any synchronized method on the same object until the current thread releases the lock. This is a simple and effective way to ensure that only one thread can access a resource at a time.
4-
How Synchronization Works
5-
Method-Level Synchronization: When a method is declared as synchronized, the thread holds the lock for the object on which the method is called. Other threads cannot call any synchronized method on the same object until the lock is released.
6-
Block-Level Synchronization: A block of code can be synchronized using the synchronized keyword. The thread holds the lock for the specified object within the block. This allows more fine-grained control over synchronization.
7-
Intrinsic Locks: Every object in Java has an intrinsic lock (or monitor lock). When a thread enters a synchronized method or block, it acquires the intrinsic lock for that object. When it exits the method or block, it releases the lock.
8-
Reentrant Locks: Java's intrinsic locks are reentrant. This means that if a thread already holds the lock for an object, it can re-enter any synchronized method or block on that object without deadlocking.
9-
Thread Safety: Synchronized methods and blocks ensure that only one thread can execute them at a time on a given object, preventing concurrent access issues and ensuring thread safety.
10-
Benefits of Synchronized
11-
Thread Safety: Synchronized methods ensure that only one thread can execute them at a time on a given object, preventing data corruption and ensuring consistency.
12-
Atomicity: Synchronized blocks provide atomicity, ensuring that a series of operations are executed as a single, indivisible step.
13-
Visibility: Changes made by one thread to a synchronized block are visible to other threads, ensuring that they see the most up-to-date state of shared variables.
1+
# Synchronized in JVM
2+
3+
## What is Synchronized?
4+
`Synchronized` is a keyword in Java that is used to mark a method or a block of code as **synchronized**. When a method is marked as synchronized, the thread that is executing it is granted a **lock** on the object the method belongs to.
5+
6+
This means that **no other thread** can execute any synchronized method on the same object until the current thread releases the lock. It ensures that only one thread can access a **shared resource** at a time.
7+
8+
---
9+
10+
## 🔹 How Synchronization Works
11+
12+
### 1️⃣ Method-Level Synchronization
13+
- When a **method** is declared as `synchronized`, the **thread** holds the lock for the **object** on which the method is called.
14+
- Other threads **cannot call** any synchronized method on the same object **until the lock is released**.
15+
16+
### 2️⃣ Block-Level Synchronization
17+
- A **block of code** can be synchronized using the `synchronized` keyword.
18+
- The **thread holds the lock** for the specified object **only within** the block.
19+
- This allows **fine-grained** control over synchronization.
20+
21+
### 3️⃣ Intrinsic Locks
22+
- Every object in Java has an **intrinsic lock** (or **monitor lock**).
23+
- When a thread enters a **synchronized method or block**, it **acquires** the **intrinsic lock** for that object.
24+
- When it exits the method or block, it **releases** the lock.
25+
26+
### 4️⃣ Reentrant Locks
27+
- Java's **intrinsic locks** are **reentrant**.
28+
- If a thread **already holds the lock** for an object, it can **re-enter** any synchronized method or block on that object **without deadlocking**.
29+
30+
### 5️⃣ Thread Safety
31+
- Synchronized methods and blocks **ensure** that **only one thread** can execute them at a time on a given object.
32+
- This prevents **concurrent access issues** and ensures **thread safety**.
33+
34+
---
35+
36+
## 🔹 Benefits of Synchronized
37+
38+
**Thread Safety**:
39+
Synchronized methods ensure that **only one thread** can execute them at a time on a given object, **preventing data corruption** and **ensuring consistency**.
40+
41+
**Atomicity**:
42+
Synchronized blocks **provide atomicity**, ensuring that a **series of operations** are executed as a **single, indivisible step**.
43+
44+
**Visibility**:
45+
Changes made by one thread to a synchronized block **are visible to other threads**, ensuring they see the **most up-to-date state** of shared variables.

src/main/kotlin/dev/shtanko/concurrency/coroutines/BackgroundTaskCancellation.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import kotlinx.coroutines.runBlocking
2525
import kotlinx.coroutines.withTimeout
2626

2727
private suspend fun longRunningTask(): String {
28-
delay(5000) // Simulate long running task
28+
delay(5000) // Simulate long-running task
2929
return "Task completed"
3030
}
3131

0 commit comments

Comments
 (0)