Monday, June 30, 2014

Operation System Interview: Locks, Deadlock, Starvation and Race Condition



https://richardbarabe.wordpress.com/2014/02/21/java-deadlock-livelock-and-lock-starvation-examples/
Deadlock happens when a process wait for another one who is using some needed resource (ie: file or database table row) to finish with it, while the other process also wait for the first process to release some other resource.
    static void transfer(BankAccount from, BankAccount to, double amount) {
        synchronized(from) {
            from.withdraw(amount);
            synchronized(to) {
                to.deposit(amount);
            }
        }
    }
        final BankAccount fooAccount = new BankAccount(1, 100d);
        final BankAccount barAccount = new BankAccount(2, 100d);
         
        new Thread() {
            public void run() {
                BankAccount.transfer(fooAccount, barAccount, 10d);
            }
        }.start();
         
        new Thread() {
            public void run() {
                BankAccount.transfer(barAccount, fooAccount, 10d);
            }
        }.start();

    static void transfer(BankAccount from, BankAccount to, double amount) {
        from.lock.lock();
            from.withdraw(amount);
            to.lock.lock();
                to.deposit(amount);
            to.lock.unlock();
        from.lock.unlock();
    }

LiveLock

with the livelock, each process is waiting “actively”, trying to resolve the problem on its own (like reverting back its work and retry). A live lock occurs when the combination of these processes’s efforts to resolve the problem makes it impossible for them to ever terminate.

two threads tries to transfer money from one account to another one at the same time. But this time, instead of waiting for a lock to be released when a required account is locked, a thread will just revert its work if any, and retry the whole operation in loop until successful :
public class BankAccount {
    double balance;
    int id;
    Lock lock = new ReentrantLock();
    BankAccount(int id, double balance) {
        this.id = id;
        this.balance = balance;
    }
    boolean withdraw(double amount) {
        if (this.lock.tryLock()) {
            // Wait to simulate io like database access ...
            try {Thread.sleep(10l);} catch (InterruptedException e) {}
            balance -= amount;
            return true;
        }
        return false;
    }
    boolean deposit(double amount) {
        if (this.lock.tryLock()) {
            // Wait to simulate io like database access ...
            try {Thread.sleep(10l);} catch (InterruptedException e) {}
            balance += amount;
            return true;
        }
        return false;
    }
    public boolean tryTransfer(BankAccount destinationAccount, double amount) {
        if (this.withdraw(amount)) {
            if (destinationAccount.deposit(amount)) {
                return true;
            } else {
                // destination account busy, refund source account.
                this.deposit(amount);
            }
        }
        return false;
    }
    public static void main(String[] args) {
        final BankAccount fooAccount = new BankAccount(1, 500d);
        final BankAccount barAccount = new BankAccount(2, 500d);
        new Thread(new Transaction(fooAccount, barAccount, 10d), "transaction-1").start();
        new Thread(new Transaction(barAccount, fooAccount, 10d), "transaction-2").start();
    }
}
class Transaction implements Runnable {
    private BankAccount sourceAccount, destinationAccount;
    private double amount;
    Transaction(BankAccount sourceAccount, BankAccount destinationAccount, double amount) {
        this.sourceAccount = sourceAccount;
        this.destinationAccount = destinationAccount;
        this.amount = amount;
    }
    public void run() {
        while (!sourceAccount.tryTransfer(destinationAccount, amount))
            continue;
        System.out.printf("%s completed ", Thread.currentThread().getName());
    }
}

Lock Starvation

Lock starvation is all about thread priority. It occurs when a thread, having lesser priority than other ones, is constantly waiting for a lock, never able to take it because other thread(s) with higher priority are constanly aquiring the lock. Suppose our bank account example. The bank adds a feature that constantly watch one’s account balance and send an email if that balance goes below zero (a monitor thread). But in this implementation, the monitor thread is of a higher priority than the transaction threads. Because of this, the transaction threads can take a very long time(say ever) to execute.

    public static void main(String[] args) {
        final BankAccount fooAccount = new BankAccount(1, 500d);
        final BankAccount barAccount = new BankAccount(2, 500d);
         
        Thread balanceMonitorThread1 = new Thread(new BalanceMonitor(fooAccount), "BalanceMonitor");
        Thread transactionThread1 = new Thread(new Transaction(fooAccount, barAccount, 250d), "Transaction-1");
        Thread transactionThread2 = new Thread(new Transaction(fooAccount, barAccount, 250d), "Transaction-2");
         
        balanceMonitorThread1.setPriority(Thread.MAX_PRIORITY);
        transactionThread1.setPriority(Thread.MIN_PRIORITY);
        transactionThread2.setPriority(Thread.MIN_PRIORITY);
         
        // Start the monitor
        balanceMonitorThread1.start();
         
        // And later, transaction threads tries to execute.
        try {Thread.sleep(100l);} catch (InterruptedException e) {}
        transactionThread1.start();
        transactionThread2.start();
    }
}
class BalanceMonitor implements Runnable {
    private BankAccount account;
    BalanceMonitor(BankAccount account) { this.account = account;}
    boolean alreadyNotified = false;
     
    @Override
    public void run() {
        System.out.format("%s started execution%n", Thread.currentThread().getName());
        while (true) {
            if(account.getBalance() <= 0) {
                // send email, or sms, clouds of smoke ...
                break;
            }
        }
        System.out.format("%s : account has gone too low, email sent, exiting.", Thread.currentThread().getName());
    }
     
}

https://docs.oracle.com/javase/tutorial/essential/concurrency/starvelive.html

Starvation

Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by "greedy" threads. For example, suppose an object provides a synchronized method that often takes a long time to return. If one thread invokes this method frequently, other threads that also need frequent synchronized access to the same object will often be blocked.

Livelock

A thread often acts in response to the action of another thread. If the other thread's action is also a response to the action of another thread, then livelock may result. As with deadlock, livelocked threads are unable to make further progress. However, the threads are not blocked — they are simply too busy responding to each other to resume work. This is comparable to two people attempting to pass each other in a corridor: Alphonse moves to his left to let Gaston pass, while Gaston moves to his right to let Alphonse pass. Seeing that they are still blocking each other, Alphone moves to his right, while Gaston moves to his left. They're still blocking each other, so...
Bloomberg LP Interview Question:
Disadvantages of locks? What is Deadlock? What is Starvation
Disadvantages of Locks: 1) adds overhead for each access, even when the chances for collision are very rare. 
2) deadlock, where two threads each hold a lock on resources that the other needs before releasing its lock. 
3) When a thread is waiting for a lock, it cannot do anything else. If a thread holding a lock is permanently blocked (due to an infinite loop, deadlock, livelock, or other liveness failure), any threads waiting for that lock will be blocked for ever, and can never make progress.
4) priority inversion: high priority threads cannot proceed if a low priority thread is holding the common lock, this effectively downgrades its priority to that of the lower-priority thread
Improvements:
Optimistic concurrency control:
We proceed with an update, hopeful that you can complete it without interference. This approach relies on collision detection to determine if there has been interference from other parties during the update, in which case the operation fails and can be retried (or not).
For example, in java, besides the heavyweight lock or synchronized, we can use atomic varibales such as AtomicInteger etc to improve concurrency. 

Deadlock: 
A deadlock occurs when two (or more) threads have created a situation where they are all blocking each other. Imagine that threads T1 and T2 need to acquire both resources A and B in order to do their work. If T1 acquires resource A, then T2 acquires resource B, T1 could then be waiting for resource B while T2 was waiting for resource A. In this case, both threads will wait indefinitely for the resource held by the other thread. These threads are said to be deadlocked.

"A deadlock cannot occur unless all of the following conditions are met: 
Protected access to shared resources, which implies waiting. 
No resource preemption, meaning that the system cannot forcibly take a resource from a thread holding it. 
Multiple independent requests, meaning a thread can hold some resources while requesting others. 
Circular dependency graph, meaning that Thread A is waiting for Thread B which is waiting for Thread C which is waiting for Thread D which is waiting for Thread A." 

Starvation: 
Reference: http://www.justanswer.com/computer/05887-major-differences-deadlock-starvation-race.html
Starvation occurs when a scheduler process (i.e. the operating system) refuses to give a particular thread any quantity of a particular resource (generally CPU). 
If there are too many high-priority threads, a lower priority thread may be starved. This can have negative impacts, though, particularly when the lower-priority thread has a lock on some resource.

Race conditions
Race conditions occurs when two thread operate on same object without proper synchronization and there operation interleaves on each other. 
Classical example of Race condition is incrementing. as increment is not an atomic operatio, if multipe threads try to incremet one variable at same time, race conditions occur

The situation where two threads compete for the same resource, where the sequence in which the resource is accessed is significant, is called race conditions. A code section that leads to race conditions is called a critical section.

References
http://www.careercup.com/question?id=15432682
http://codeidol.com/community/java/disadvantages-of-locking/13510/

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts