ProgramGuru

Flows and safe shared state

Flows and safe shared state

A stream can produce several values over time rather than return one final number. Flow represents asynchronous sequences of values. A simple flow builder is cold: its body starts when collected and normally runs again for a new collection. Hot shared streams have different lifetime and replay contracts.

What you’ll learn

  • Collect a cold stream and coordinate a shared counter.
  • Distinguish a mutex from task completion.

Flows and safe shared state in the example

  1. Collect cold stream

    A stream can produce several values over time rather than return one final number.

    Collect cold stream
  2. Coordinate updates

    Emit supplies a value and collect processes emitted values.

    Coordinate updates
  3. Read after child scope

    Collection prints the two emitted scores in their flow order.

    Read after child scope
Collection prints the two emitted scores in their flow order. The scope waits for both child updates, and the mutex protects each count change. The final count is two. This is a small state demonstration, not a claim about throughput or production architecture.

Understand flows and safe shared state

Emit supplies a value and collect processes emitted values. Suspending collection can wait without replacing the stream with a precomputed list. Shared counters still need a synchronization policy such as Mutex or confinement; merely using coroutines does not make increments safe.

Connect this to read-only and mutable lists and structured concurrency and cancellation.

Follow flows and safe shared state

Save the complete source as Main.kt and use the Kotlin/JVM environment. Use a local Kotlin/JVM project with org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2. This example requires the library on both compile and runtime classpaths. See coroutine setup.

Kotlin/JVM · complete program

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
fun main() = runBlocking {
  flow { emit(70); emit(80) }.collect { println("Score: $it") }
  val mutex = Mutex()
  var count = 0
  coroutineScope {
    repeat(2) { launch { mutex.withLock { count++ } } }
  }
  println("Count: $count")
}

Expected output

Score: 70
Score: 80
Count: 2

Follow the execution

Collection prints the two emitted scores in their flow order. The scope waits for both child updates, and the mutex protects each count change. The final count is two. This is a small state demonstration, not a claim about throughput or production architecture.

Practice: Flows and safe shared state

Your task: Does every new collection of this cold builder reuse one already-running producer?

Kotlin/JVM · complete program

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
fun main() = runBlocking {
  flow { emit(70); emit(80) }.collect { println("Score: $it") }
  val mutex = Mutex()
  var count = 0
  coroutineScope {
    repeat(2) { launch { mutex.withLock { count++ } } }
  }
  println("Count: $count")
}

Worked answer

No. A new collection runs the builder again. SharedFlow and StateFlow provide different hot-stream contracts when shared state or broadcast behaviour is intended.

Flows and safe shared state: the important boundary

Keep lock scopes small and avoid pretending a series of separate locked operations is one transaction. Buffering, context changes, state replay and cancellation affect real stream behaviour; select those policies explicitly.

Check your understanding

Does every new collection of this cold builder reuse one already-running producer?
Choose one answer. You can try again.

Answer and explanation

No.

No. A new collection runs the builder again. SharedFlow and StateFlow provide different hot-stream contracts when shared state or broadcast behaviour is intended.

Check the practical consequence

A lock protects each increment. What else is needed before reporting the final count?
Choose one answer. You can try again.

Answer and explanation

Ensure the child operations have completed, as the enclosing scope does here.

Collect a cold stream and coordinate a shared counter. Distinguish a mutex from task completion.

Flows and safe shared state: what to keep in mind

No. A new collection runs the builder again. SharedFlow and StateFlow provide different hot-stream contracts when shared state or broadcast behaviour is intended. Keep lock scopes small and avoid pretending a series of separate locked operations is one transaction.

Further reading: official Kotlin documentation for this topic.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Build a validated student report →