WikiGalaxy

Personalize

Concurrency Issues in Operating Systems

Overview:

Concurrency issues in operating systems arise when multiple processes or threads execute simultaneously, potentially leading to conflicts over shared resources. These issues can result in unexpected behavior, data corruption, or system crashes if not managed properly.

Common Concurrency Issues:

  • Race Conditions
  • Deadlocks
  • Starvation
  • Priority Inversion

Race Conditions

Understanding Race Conditions:

Race conditions occur when two or more threads access shared data and try to change it simultaneously. The final outcome depends on the sequence of thread execution, which can lead to unpredictable results.


class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

class RaceConditionDemo implements Runnable {
    private Counter counter;

    public RaceConditionDemo(Counter counter) {
        this.counter = counter;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            counter.increment();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(new RaceConditionDemo(counter));
        Thread t2 = new Thread(new RaceConditionDemo(counter));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Final count: " + counter.getCount());
    }
}
        

Why It Occurs:

Race conditions occur due to a lack of proper synchronization mechanisms like locks or semaphores, allowing multiple threads to modify shared data concurrently.

Prevention Techniques:

  • Use locks to ensure mutual exclusion.
  • Implement atomic operations.
  • Utilize thread-safe data structures.

Console Output:

Final count: 2000

Deadlocks

Understanding Deadlocks:

Deadlocks occur when two or more processes are unable to proceed because each is waiting for the other to release resources. This results in a standstill where no progress is made.


class DeadlockDemo {
    static class Resource {
        public synchronized void acquire(Resource other) {
            System.out.println(Thread.currentThread().getName() + " acquired " + this);
            other.release();
        }

        public synchronized void release() {
            System.out.println(Thread.currentThread().getName() + " released " + this);
        }
    }

    public static void main(String[] args) {
        final Resource resource1 = new Resource();
        final Resource resource2 = new Resource();

        Thread t1 = new Thread(() -> resource1.acquire(resource2), "Thread-1");
        Thread t2 = new Thread(() -> resource2.acquire(resource1), "Thread-2");

        t1.start();
        t2.start();
    }
}
        

Why It Occurs:

Deadlocks occur due to circular wait, hold and wait, no preemption, and mutual exclusion conditions.

Prevention Techniques:

  • Use a resource hierarchy to prevent circular wait.
  • Implement a timeout mechanism for resource acquisition.
  • Apply deadlock detection and recovery algorithms.

Console Output:

Thread-1 acquired Resource@5e91993f Thread-2 acquired Resource@1d44bcfa

Starvation

Understanding Starvation:

Starvation occurs when a process is perpetually denied necessary resources to proceed, often due to resource allocation policies that prioritize other processes.


class StarvationDemo {
    static class Resource {
        public synchronized void use() {
            System.out.println(Thread.currentThread().getName() + " using resource");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        final Resource resource = new Resource();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(resource::use, "Thread-" + i);
            thread.setPriority(i == 9 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY);
            thread.start();
        }
    }
}
        

Why It Occurs:

Starvation occurs due to unfair resource scheduling policies that favor certain processes over others.

Prevention Techniques:

  • Implement fair scheduling algorithms like Round Robin.
  • Use aging techniques to increase the priority of starving processes.

Console Output:

Thread-0 using resource Thread-1 using resource ... Thread-9 using resource

Priority Inversion

Understanding Priority Inversion:

Priority inversion occurs when a higher-priority task is waiting for a lower-priority task to release a resource. This can cause significant delays in high-priority task execution.


class PriorityInversionDemo {
    static class SharedResource {
        public synchronized void access() {
            System.out.println(Thread.currentThread().getName() + " accessing resource");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        final SharedResource resource = new SharedResource();

        Thread lowPriority = new Thread(resource::access, "Low-Priority");
        lowPriority.setPriority(Thread.MIN_PRIORITY);

        Thread mediumPriority = new Thread(() -> System.out.println("Medium-Priority running"), "Medium-Priority");
        mediumPriority.setPriority(Thread.NORM_PRIORITY);

        Thread highPriority = new Thread(resource::access, "High-Priority");
        highPriority.setPriority(Thread.MAX_PRIORITY);

        lowPriority.start();
        highPriority.start();
        mediumPriority.start();
    }
}
        

Why It Occurs:

Priority inversion occurs due to improper scheduling when a lower-priority task holds a lock needed by a higher-priority task.

Prevention Techniques:

  • Use priority inheritance protocols.
  • Implement priority ceiling protocols.

Console Output:

Low-Priority accessing resource Medium-Priority running High-Priority accessing resource

Synchronization Techniques

Overview:

Synchronization techniques are essential to prevent concurrency issues by controlling the access of multiple threads to shared resources.


class SynchronizedCounter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

class SyncDemo implements Runnable {
    private SynchronizedCounter counter;

    public SyncDemo(SynchronizedCounter counter) {
        this.counter = counter;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            counter.increment();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedCounter counter = new SynchronizedCounter();
        Thread t1 = new Thread(new SyncDemo(counter));
        Thread t2 = new Thread(new SyncDemo(counter));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Final count: " + counter.getCount());
    }
}
        

Why Synchronization is Important:

Synchronization prevents race conditions by ensuring that only one thread accesses a critical section at a time.

Common Synchronization Techniques:

  • Locks
  • Semaphores
  • Monitors

Console Output:

Final count: 2000

Locking Mechanisms

Overview:

Locking mechanisms are used to control access to shared resources in concurrent programming, ensuring data integrity and consistency.


import java.util.concurrent.locks.ReentrantLock;

class LockCounter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

class LockDemo implements Runnable {
    private LockCounter counter;

    public LockDemo(LockCounter counter) {
        this.counter = counter;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            counter.increment();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockCounter counter = new LockCounter();
        Thread t1 = new Thread(new LockDemo(counter));
        Thread t2 = new Thread(new LockDemo(counter));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Final count: " + counter.getCount());
    }
}
        

Why Locks are Used:

Locks provide a mechanism to enforce mutual exclusion, ensuring that only one thread can access a critical section at a time.

Types of Locks:

  • Reentrant Locks
  • Read-Write Locks
  • Spin Locks

Console Output:

Final count: 2000

logo of wikigalaxy

Newsletter

Subscribe to our newsletter for weekly updates and promotions.

Privacy Policy

 • 

Terms of Service

Copyright © WikiGalaxy 2025