Dataford
Interview QuestionsInterview GuidesExperiencesCoaching
New
Pricing
Get started
Dataford
Popular roles
Software EngineerData AnalystData ScientistData EngineerBusiness AnalystAI EngineerMachine Learning EngineerProduct Manager
Browse
Browse All RolesEvery role hub, from analyst to MLBrowse All CompaniesCompany-specific interview loopsAll Interview GuidesThe full guide library
Top questions by role
Software EngineerData AnalystData ScientistData EngineerBusiness AnalystAI EngineerMachine Learning EngineerProduct Manager
Top questions by skill
SQLPythonStatisticsMachine LearningA/B TestingSystem DesignGenerative AIProduct SenseMetricsBehavioral
Browse all questions →Try a mock interview
Experiences
Practice
Mock InterviewsTimed interview simulations with feedbackCoaching1:1 coaching with people at your target companiesSuccess PathYour 6-week structured planModulesCurated lessons by topicWebinarsTalks from ex-Big Tech data leadsPlaygroundA free-form scratch editor
Learn
BlogInterview strategy and career adviceTech Job Market ReportHiring trends across data and AI rolesFor UniversitiesDataford for career centersAbout DatafordWho we are and how we build
Pricing
Build my plan

Fix Multithreading Issues

MediumCoding00:00
Practice interviewer
In session
5 left
00:00

Your question is Fix Multithreading Issues. Take a moment with it on the right.

Talk me through your thinking if you like. When you're confident, submit your answer and I'll grade it like a real screen (7/10 or better passes).

You need to log in / sign up to chat or submit.

Problem

You are reviewing the Kotlin class below from an Android banking app. It caches exchange rates, notifies screens when a rate changes, and moves money between in-memory accounts. It passes a single-threaded demo but crashes and shows wrong numbers in production.

Find every multithreading issue in it. For each one, explain what can go wrong at runtime and show how you would fix it.

interface RatesListener {
    // Implemented by Activities. Updates views.
    fun onRateUpdated(pair: String, rate: Rate)
}

class Account(val id: String, var balance: BigDecimal)

class RatesRepository(private val api: RatesApi) {

    private val cache = HashMap<String, Rate>()
    private val listeners = ArrayList<RatesListener>()
    var refreshCount = 0

    fun addListener(listener: RatesListener) {
        listeners.add(listener)
    }

    fun refresh(pairs: List<String>) {
        for (pair in pairs) {
            Thread {
                // blocking network call
                val rate = api.fetchRate(pair)
                cache[pair] = rate
                refreshCount++
                for (listener in listeners) {
                    listener.onRateUpdated(pair, rate)
                }
            }.start()
        }
    }

    fun getRate(pair: String): Rate? {
        if (!cache.containsKey(pair)) {
            cache[pair] = api.fetchRate(pair)
        }
        return cache[pair]
    }

    fun transfer(from: Account, to: Account, amount: BigDecimal) {
        synchronized(from) {
            synchronized(to) {
                from.balance -= amount
                to.balance += amount
            }
        }
    }
}

Assume refresh, getRate, addListener and transfer can all be called from different threads, including the main thread, and that refresh can be called again before the previous one finishes.