Threads and Executer framework %28Runnable%2C Callable V %29%2C Object Serialization%2C Externalization - Yash-777/MyWorld GitHub Wiki

Serialization is a mechanism to transform a graph of Java objects into an array of bytes for storage(to disk file) or transmission(across a network), then by using deserialization we can restore the graph of objects. Graphs of objects are restored correctly using a reference sharing mechanism. But before storing, check whether serialVersionUID from input-file/network and .class file serialVersionUID are the same. If not, throw a java.io.InvalidClassException.

Note - It is strongly recommended that all serializable classes explicitly declare a serialVersionUID, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail.


Threads

Lifecycle of a Thread

When a thread is created, a new thread of control is added to the current process. Every process has at least one thread of control, in the program's main() routine. Each thread in the process runs simultaneously, and has access to the calling process's global data. In addition each thread has its own private attributes and call stack.

If an exception occurs in a Thread t1. JVM will terminate the t1 and will not carryforward the exception to main-Deamon thread. In order to catch the exception use t.setUncaughtExceptionHandler(h);

Variable Volatile: Volatile Keyword is applicable to variables. volatile keyword in Java guarantees that value of the volatile variable will always be read from main memory and not from Thread's local cache.

Thread CPU cache stackpost
Can we call Thread.start() multiple times?

We can call but it leads to java.lang.IllegalThreadStateException. Its better not to call start() method multiple times.

public class ThreadTest {
    public static void main(String[] args) {
        ThreadCls t = new ThreadCls();
        t.run();    // normal function call
        t.start(); // Starts new Thread
        t.start(); // java.lang.IllegalThreadStateException
    }
}
class ThreadCls extends Thread {
    @Override
    public void run() {
        while(true) {
            System.out.println("---");
            try {
                Thread.sleep(1000 * 3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Threads, Multithreading, DeadLock, Join, Synchronized, volatile

Thread: Group of statements that are executed separately. Thread is a "light weight processor" which share common features of a processor.

User Defined Thread can be created in 2 ways

  1. Extends Thread Class
  2. Implements Runnable interface: As we know java doesnot support multiple inheritance. If we extends thread class the we dont have chance to extend other class. In this scenario we go for Runnable interface.
Class MyClass extends Thread, SuperClass   {WRONG}
Class MyClass extends SuperClass implements Runnable
public class HelloThread extends Thread {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new HelloThread()).start();
    }
}
public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

Thread.sleep causes the current thread to suspend execution for a specified time period. Thread.sleep(4000); //Pause for 4 seconds

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,t.join(); causes the current thread to pause execution until t's thread terminates. Overloads of join allow the programmer to specify a waiting period. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.

Volatile: Applicable to variables If we do not declare variables as volatile then every thread will have their own copy. If T1 changes the value, T2 canot access the updated value which may lead to "Data-Inconsistency" [Ex: Bank amount transfer form one account to another] Similar to Static variable(class level-Method Area) but volatile applicable of instance objects(Instance-Heap Area).

Synchronization: Threads communicate primarily by sharing access to fields and the objects reference fields refer to. applicable to Methods and Blocks. Block/Method gets executed only by 1 thread at a time. If T1 takes control of Synchronized block T2 canot access it untill T1 releases the lock. [Ex: Bank transaction form ATM,NET Banking, UPI, etc..,)

Deadlock: Deadlock describes a situation where two or more threads are blocked forever, waiting for each other.

Deadlock occurs when multiple threads need the same locks but obtain them in different order.

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1(); // Lock1, Lock2
      ThreadDemo2 T2 = new ThreadDemo2(); // Lock2, Lock1
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try { Thread.sleep(10); } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try { Thread.sleep(10); } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Thread - Runnable(@run():void) (vs) Callable(@call():Object) comes into point when we are using Executer framework.

Executer frameworkstack post

Executor Interfacesjava tutorial

The java.util.concurrent package defines three executor interfaces:

  • Executor, a simple interface that supports launching new tasks.
  • ExecutorService, a subinterface of Executor, which adds features that help manage the life cycle, both of the individual tasks and of the executor itself.
  • ScheduledExecutorService, a subinterface of ExecutorService, supports future and/or periodic execution of tasks.

ExecutorService is a subinterface of Executor, which accepts both Runnable and Callable tasks.

Earlier Multi-Threading can be achieved using Interface RunnableSince 1.0, but here the problem is after completing the thread task we are unable to collect the Threads information. In-order to collect the data we may use Static fields.

Example Separate threads to collect each student data.

static HashMap<String, List> multiTasksData = new HashMap();
public static void main(String[] args) {
    Thread t1 = new Thread( new RunnableImpl(1), "T1" );
    Thread t2 = new Thread( new RunnableImpl(2), "T2" );
    Thread t3 = new Thread( new RunnableImpl(3), "T3" );

    multiTasksData.put("T1", new ArrayList() ); // later get the value and update it.
    multiTasksData.put("T2", new ArrayList() );
    multiTasksData.put("T3", new ArrayList() );
}

To resolve this problem they have introduced Callable<V>Since 1.5 which returns a result and may throw an exception.

  • Single Abstract Method : Both Callable and Runnable interface have a single abstract method, which means they can be used in lambda expressions in java 8.

    public interface Runnable {
    public void run();
    }
    
    public interface Callable<Object> {
        public Object call() throws Exception;
    }

There are a few different ways to delegate tasks for execution to an ExecutorService.

  • execute(Runnable task):void crates new thread but not blocks main thread or caller thread as this method return void.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?> crates new thread and blocks main thread when you are using future.get().

Example of using Interfaces Runnable, Callable with Executor framework.

class CallableTask implements Callable<Integer> {
    private int num = 0;
    public CallableTask(int num) {
        this.num = num;
    }
    @Override
    public Integer call() throws Exception {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");
        
        for (int i = 0; i < 5; i++) {
            System.out.println(i + " : " + threadName + " : " + num);
            num = num + i;
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task. Final Value : "+ num);
        
        return num;
    }
}
class RunnableTask implements Runnable {
    private int num = 0;
    public RunnableTask(int num) {
        this.num = num;
    }
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");
        
        for (int i = 0; i < 5; i++) {
            System.out.println(i + " : " + threadName + " : " + num);
            num = num + i;
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task. Final Value : "+ num);
    }
}
public class MainThread_Wait_TillWorkerThreadsComplete {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Main Thread start...");
        Instant start = java.time.Instant.now();
        
        runnableThreads();
        callableThreads();
        
        Instant end = java.time.Instant.now();
        Duration between = java.time.Duration.between(start, end);
        System.out.format("Time taken : %02d:%02d.%04d \n", between.toMinutes(), between.getSeconds(), between.toMillis()); 
        
        System.out.println("Main Thread completed...");
    }
    public static void runnableThreads() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<?> f1 = executor.submit( new RunnableTask(5) );
        Future<?> f2 = executor.submit( new RunnableTask(2) );
        Future<?> f3 = executor.submit( new RunnableTask(1) );
        
        // Waits until pool-thread complete, return null upon successful completion.
        System.out.println("F1 : "+ f1.get());
        System.out.println("F2 : "+ f2.get());
        System.out.println("F3 : "+ f3.get());
        
        executor.shutdown();
    }
    public static void callableThreads() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<Integer> f1 = executor.submit( new CallableTask(5) );
        Future<Integer> f2 = executor.submit( new CallableTask(2) );
        Future<Integer> f3 = executor.submit( new CallableTask(1) );
    
        // Waits until pool-thread complete, returns the result.
        System.out.println("F1 : "+ f1.get());
        System.out.println("F2 : "+ f2.get());
        System.out.println("F3 : "+ f3.get());
        
        executor.shutdown();
    }
}

Multi Threads - Any Exception occurs then JVM terminates only that thread.

T1.wait() - then T1.notify() - Wakes up a single thread that is waiting on this object's monitor.
If no T1 is not in wait() state, If you are using T1.notify() then use java.lang.IllegalMonitorStateException

class MyThread implements Runnable {
    private final Object counterLock = new Object();
    
    public static void threadSleep(int sec) {
        try {
            Thread.sleep(1000 * sec);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        Thread currentThread = Thread.currentThread();
        String name = currentThread.getName();
        synchronized (counterLock) {
            System.out.println("Object Lock wait: " +name);
            threadSleep(2);
        }
        synchronized (currentThread) {
            System.out.println("Current Thread wait:" +name); //currentThread.wait(1000 * 1);
            if(name.contains("0")) System.out.println("Thread-0 :: Arthmetic Excepiton:"+ 1/0); 
        }
    }
}
public class ThreadTest  {
    public static void main(String[] args) throws InterruptedException {
        
        System.out.println("Main Thread start...");
        
        MyThread threadTest = new MyThread(); // Threads work on same object
        Thread th = new Thread( threadTest );    th.start();
        Thread th2 = new Thread( threadTest );    th2.start();
        th.join();
        th2.join();
        System.out.println("Main Thread End.");
    }
}

output:

Main Thread start...
Object Lock wait: Thread-0
Current Thread wait:Thread-0
Exception in thread "Thread-0" Object Lock wait: Thread-1
java.lang.ArithmeticException: / by zero
	at com.github.yash777.Qualification.MyThread.run(ThreadTest.java:26)
	at java.lang.Thread.run(Unknown Source)
Current Thread wait:Thread-1
Main Thread End.

⚠️ **GitHub.com Fallback** ⚠️