Irbisa · Cheatsheet
Java Concurrency & Collections
Threads and the Java memory model, volatile and synchronized, locks, executors, concurrent collections, HashMap internals
What is the difference between a process and a thread, and which states can a JVM thread be in?
Where do local variables, primitives and objects live in the JVM, stack or heap, and which of them can two threads share?
fun countInParallel(): Int { var count = 0 // captured var: becomes a Ref.IntRef on the heap val workers = List(4) { Thread { repeat(100_000) { count++ } } } workers.forEach { it.start() }…Before coroutines, how did Android apps move work off the main thread (
Thread,HandlerThread,Executor,AsyncTask), and why wasAsyncTaskdeprecated?// Java-era pattern: an executor for the work, a main-thread Handler for the resultclass UserLoader(private val api: UserApi) { private val executor = Executors.newSingleThreadExecutor() private val main = Handler(Looper.getMainLooper()) fun load(id: Long, onResult: (User) -> Unit) {…ArrayListvsLinkedList: what doget,addandremovecost in each, and why isArrayListalmost always the faster choice?fun demo() { val linked = LinkedList((1..20_000).toList()) var sum = 0L for (i in linked.indices) sum += linked[i] // wrong: each get(i) walks the chain, O(n²) for (x in linked) sum += x // right: one walk, O(n)…HashMap,LinkedHashMaporTreeMap(and their sets): what order and lookup cost does each give, and which one doesmutableMapOf()return?fun demo() { val scores = mapOf("mia" to 1, "zoe" to 3, "adam" to 5) // LinkedHashMap println(scores.keys) // [mia, zoe, adam]: insertion order println(scores.toSortedMap().keys) // [adam, mia, zoe]: TreeMap println(hashMapOf("mia" to 1, "zoe" to 3, "adam" to 5).keys) // [zoe, adam, mia]: bucket order…How does
HashMapwork internally: buckets,hashCodeandequals, collisions, load factor, resizing and treeification?What is happens-before in the Java Memory Model, and what do
synchronized,@VolatileandAtomicIntegereach guarantee?class Stats { private val lock = Any() private var total = 0L // guarded by lock @Volatile var lastUser: String? = null // one writer: visibility is enough private val hits = AtomicInteger() // atomic read-modify-write…Find the bug:
stop()setsrunning = false, but sometimes the worker thread never leaves its loop.class Worker { private var running = true private var processed = 0L fun start() { Thread { while (running) { processed++ // stands in for work with no calls or locks } println("stopped after $processed steps") }.start() } fun stop() { running = false // called from the main thread }}How do you stop a thread correctly, what does
interrupt()actually do, and why must you never swallowInterruptedException?class Uploader(private val queue: BlockingQueue<File>) { private val worker = Thread { try { while (!Thread.currentThread().isInterrupted) { upload(queue.take()) // take() throws InterruptedException }…synchronizedvsReentrantLockvsReentrantReadWriteLock: what does an explicit lock add, and what does reentrant mean?class TokenStore { private val lock = ReentrantLock() private var token: String? = null fun tryRead(): String? = if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {…Two threads call
transfer(a, b)andtransfer(b, a)at the same moment and both freeze forever. Why, and how do lock ordering andtryLockprevent it?class Account(val id: Long, var balance: Long) fun transfer(from: Account, to: Account, amount: Long) { synchronized(from) { synchronized(to) { from.balance -= amount to.balance += amount } }} // thread 1: transfer(a, b, 10)// thread 2: transfer(b, a, 5)How do you hand work from producer threads to a consumer thread:
wait/notifyor aBlockingQueue, and why mustwait()sit in a loop?val jobs = ArrayBlockingQueue<Job>(100) fun produce(job: Job) = jobs.put(job) // blocks while 100 jobs are waitingfun consumeLoop() { while (true) process(jobs.take()) // blocks while the queue is empty}…How does a thread pool work, how do
newFixedThreadPool,newCachedThreadPoolandnewSingleThreadExecutordiffer, and what happens when tasks pile up?val pool = ThreadPoolExecutor( 2, 4, // core and maximum threads 30, TimeUnit.SECONDS, // idle extra threads stop after 30 s ArrayBlockingQueue(100), // bounded: at most 100 waiting tasks ThreadPoolExecutor.CallerRunsPolicy(), // full: the caller runs the task itself)…ConcurrentHashMap,Collections.synchronizedMapandCopyOnWriteArrayList: how does each make access thread-safe, and what does each cost?val counts = ConcurrentHashMap<String, Int>()fun record(event: String) { counts.merge(event, 1, Int::plus) // atomic: no lost increments} val sessions = ConcurrentHashMap<String, Session>()…Find the bug: two screens ask for the same user at once and the cache loads it from the network twice, although the map is a
ConcurrentHashMap.class UserCache(private val api: UserApi) { private val users = ConcurrentHashMap<Long, User>() // Called from several threads at once. fun user(id: Long): User = users.getOrPut(id) { api.loadUser(id) } // blocking network call}How do CAS-based atomics like
AtomicReferencework without a lock, and where do they stop helping: contention, ABA, multi-field updates?data class Stats(val count: Int, val total: Long) class Metrics { private val stats = AtomicReference(Stats(0, 0)) fun record(ms: Long) {…What can go wrong when two threads write to a plain
HashMapat the same time, and why is wrapping onlyputinsynchronizednot enough?class SessionRegistry { private val lock = Any() private val sessions = HashMap<String, Session>() // wrong: writers take the lock, the reader does not fun addRacy(id: String, s: Session) = synchronized(lock) { sessions[id] = s }…What is
ThreadLocal, how does it leak or hand stale values to the next task on a thread pool, and what changes inside coroutines?private val requestId = ThreadLocal<String?>() // On a pool: always clean up, or the next task on this thread inherits the valuefun handle(pool: ExecutorService, id: String, work: () -> Unit) = pool.execute { requestId.set(id) try {…