vasanth0989 / prep-myself

0 stars 0 forks source link

Introduction to Threads #8

Open vasanth0989 opened 10 months ago

vasanth0989 commented 10 months ago

Image

vasanth0989 commented 10 months ago

windows xp puppy animation

vasanth0989 commented 10 months ago

Image

vasanth0989 commented 10 months ago
vasanth0989 commented 10 months ago

Different States of Thread

In Java, threads can have several different states during their lifecycle. These states represent the current condition or phase of a thread's execution. The different thread states in Java are:

New: When a thread is created using the new keyword or by instantiating a class that extends the Thread class, it is in the "New" state. At this point, the thread has not yet started executing.

Runnable: The thread enters the "Running" state when the CPU scheduler selects it for execution. In this state, the thread's code is actively being executed.

Blocked/Waiting: A thread can enter the "Blocked" or "Waiting" state when it is temporarily unable to continue its execution. This can happen for various reasons, such as waiting for I/O operations to complete, waiting for a lock, or explicitly being put to sleep by calling methods like Thread.sleep() or Object.wait().

Timed Waiting: This is a variation of the "Waiting" state where a thread waits for a specified amount of time before transitioning to the "Runnable" state again. This can be achieved using methods like Thread.sleep() with a specified time or Object.wait() with a timeout.

Terminated: A thread enters the "Terminated" state when it has completed its execution or when it has been explicitly stopped. Once a thread is terminated, it cannot be restarted.

These thread states represent the different phases a thread can go through in its lifecycle. Java provides methods and mechanisms for managing and controlling the state transitions of threads to ensure proper synchronization and coordination in multithreaded applications.

vasanth0989 commented 10 months ago
public class TestMain {

    public static void main(String[] args) {
        // Two ways of creating Threads
        // One is basically class Extends Thread Class
        // Class implementing Runnable interface
        // Thread.currentThread() - this will always represent the Thread which is executing this statement
        System.out.println("Main Thread state:" + Thread.currentThread().getState());
        System.out.println("Is Thread Daemon:" + Thread.currentThread().isDaemon()); // return false, since main thread is non-daemon thread
        SimpleThread thread = new SimpleThread();
        // when we set the 'daemon' property as 'true' these becomes background threads
//        thread.setDaemon(true);
        thread.setName("My custom Thread");
        System.out.println("Custom Thread State:" + thread.getState().toString());
        thread.start();

        // There are 2 different types of Threads
        // 01 - Daemon Thread
        // 02 - Non Daemon Thread
        // By default main thread is a 'Non Daemon Thread'
        // Any thread that you are creating with 'new' operator will also be 'Non Daemon Thread'
        // What is the nature of 'non-Daemon Thread' - Until the non-daemon thread gets executed your JVM process will not shut down.
        // Whereas for Daemon Thread - these are also called as 'background' thread which will exit if all non-Daemon threads are
        // completed its execution

        // Another way of Creating Thread
        // In the constructor argument you have to pass the Runnable object
        Thread th = new Thread(new IamRunnable());
        th.setName("Custom Runnable Thread");
//        th.setDaemon(true);
        th.start();
        System.out.println("This is from " + Thread.currentThread().getName());
    }
}

class SimpleThread extends Thread {
    @Override
    public void run() {
        try {
            sleep(1000);
            System.out.println("Executing Thread name" + Thread.currentThread().getName());
            System.out.println(Thread.currentThread().getName() + "State:" + this.getState().name());
        } catch (Exception ex) {

        }
    }

}

class IamRunnable implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println("Executing Thread from Runnable:" + Thread.currentThread().getName());
            System.out.println(Thread.currentThread().getName() + "State:" + Thread.currentThread().getState().name());
        } catch (Exception ex) {

        }
    }
}
vasanth0989 commented 10 months ago

Image

vasanth0989 commented 10 months ago

Synchronized Example

public class TestMain {

    public static void main(String[] args) throws Exception {
        Task task = new Task();
        Worker w1 = new Worker(task);
        Worker w2 = new Worker(task);
        Thread th1 = new Thread(w1);
        Thread th2 = new Thread(w2);
        th1.start();
        th2.start();
        th1.join();
        th2.join();
        // Example for race condition
        // Where 2 threads accessing the same data
        // Without proper synchronization this will always run into a problem, where we won't get desired result
        // The output here will not be 20_000
        System.out.println("Task Completed total count:"+task.getCount());
        // How we can make this work???
    }
}

class Worker implements Runnable
{
    private final Task task;

    public Worker(Task task){
       this.task = task;
   }

    @Override
    public void run() {
        task.increment();
    }
}

class Task {
    private int count;

    public int getCount() {
        return count;
    }

    public synchronized void increment(){
        for(int i=0;i<10_000;i++){
            count += 1;
        }
    }
}
vasanth0989 commented 10 months ago

DeadLock Example

public class DeadlockExample {
    public static void main(String[] args) {
        ReentrantLock lock1 = new ReentrantLock();
        ReentrantLock lock2 = new ReentrantLock();

        Thread thread1 = new Thread(() -> {
            lock1.lock();
            try {
                // Simulate some work
                Thread.sleep(100);
                System.out.println("Thread 1: Holding lock 1...");

                // Attempt to acquire lock 2
                lock2.lock();
                System.out.println("Thread 1: Acquired lock 2!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock1.unlock();
                lock2.unlock();
            }
        });

        Thread thread2 = new Thread(() -> {
            lock2.lock();
            try {
                // Simulate some work
                Thread.sleep(100);
                System.out.println("Thread 2: Holding lock 2...");

                // Attempt to acquire lock 1
                lock1.lock();
                System.out.println("Thread 2: Acquired lock 1!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock1.unlock();
                lock2.unlock();
            }
        });

        thread1.start();
        thread2.start();
    }
}
vasanth0989 commented 10 months ago

Image

vasanth0989 commented 10 months ago

Fixing the deadLock

public class DeadlockExample {
    public static void main(String[] args) {
        ReentrantLock lock1 = new ReentrantLock();
        ReentrantLock lock2 = new ReentrantLock();

        Thread thread1 = new Thread(() -> {
            lock1.lock();
            try {
                // Simulate some work
                Thread.sleep(100);
                System.out.println("Thread 1: Holding lock 1...");

                // Attempt to acquire lock 2
                lock2.lock();
                System.out.println("Thread 1: Acquired lock 2!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock1.unlock();
                lock2.unlock();
            }
        });

        Thread thread2 = new Thread(() -> {
            lock1.lock();
            try {
                // Simulate some work
                Thread.sleep(100);
                System.out.println("Thread 2: Holding lock 2...");

                // Attempt to acquire lock 1
                lock2.lock();
                System.out.println("Thread 2: Acquired lock 1!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock1.unlock();
                lock2.unlock();
            }
        });

        thread1.start();
        thread2.start();
    }
}