Author: saqibkhan

  • Condition Interface

    A java.util.concurrent.locks.Condition interface provides a thread ability to suspend its execution, until the given condition is true. A Condition object is necessarily bound to a Lock and to be obtained using the newCondition() method.

    Condition Methods

    Following is the list of important methods available in the Condition class.

    Sr.No.Method & Description
    1public void await()Causes the current thread to wait until it is signalled or interrupted.
    2public boolean await(long time, TimeUnit unit)Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses.
    3public long awaitNanos(long nanosTimeout)Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses.
    4public long awaitUninterruptibly()Causes the current thread to wait until it is signalled.
    5public long awaitUntil()Causes the current thread to wait until it is signalled or interrupted, or the specified deadline elapses.
    6public void signal()Wakes up one waiting thread.
    7public void signalAll()Wakes up all waiting threads.

    Example

    The following TestThread program demonstrates these methods of the Condition interface. Here we’ve used signal() to notify and await() to suspend the thread.

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TestThread {
    
       public static void main(String[] args) throws InterruptedException {
    
      ItemQueue itemQueue = new ItemQueue(10);
      //Create a producer and a consumer.
      Thread producer = new Producer(itemQueue);
      Thread consumer = new Consumer(itemQueue);
      //Start both threads.
      producer.start();
      consumer.start();
      //Wait for both threads to terminate.
      producer.join();
      consumer.join();
    } static class ItemQueue {
      private Object[] items = null;
      private int current = 0;
      private int placeIndex = 0;
      private int removeIndex = 0;
      private final Lock lock;
      private final Condition isEmpty;
      private final Condition isFull;
      public ItemQueue(int capacity) {
         this.items = new Object[capacity];
         lock = new ReentrantLock();
         isEmpty = lock.newCondition();
         isFull = lock.newCondition();
      }
      public void add(Object item) throws InterruptedException {
         lock.lock();
         while(current >= items.length)
            isFull.await();
         items[placeIndex] = item;
         placeIndex = (placeIndex + 1) % items.length;
         ++current;
         //Notify the consumer that there is data available.
         isEmpty.signal();
         lock.unlock();
      }
      public Object remove() throws InterruptedException {
         Object item = null;
         lock.lock();
         while(current <= 0) {
            isEmpty.await();
         }
         item = items[removeIndex];
         removeIndex = (removeIndex + 1) % items.length;
         --current;
         //Notify the producer that there is space available.
         isFull.signal();
         lock.unlock();
         return item;
      }
      public boolean isEmpty() {
         return (items.length == 0);
      }
    } static class Producer extends Thread {
      private final ItemQueue queue;
      
      public Producer(ItemQueue queue) {
         this.queue = queue;
      }
      @Override
      public void run() {
         String[] numbers =
            {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
         try {
            
            for(String number: numbers) {
               System.out.println("[Producer]: " + number);
            }
            queue.add(null);
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         } 
      }
    } static class Consumer extends Thread {
      private final ItemQueue queue;
      
      public Consumer(ItemQueue queue) {
         this.queue = queue;
      }
      @Override
      public void run() {
         
         try {
            
            do {
               Object number = queue.remove();
               System.out.println("[Consumer]: " + number);
               if(number == null) {
                  return;
               }
            } while(!queue.isEmpty());
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         }
      }
    } }

    This will produce the following result.

    Output

    [Producer]: 1
    [Producer]: 2
    [Producer]: 3
    [Producer]: 4
    [Producer]: 5
    [Producer]: 6
    [Producer]: 7
    [Producer]: 8
    [Producer]: 9
    [Producer]: 10
    [Producer]: 11
    [Producer]: 12
    [Consumer]: null
    
  • ReadWriteLock Interface

    A java.util.concurrent.locks.ReadWriteLock interface allows multiple threads to read at a time but only one thread can write at a time.

    • Read Lock − If no thread has locked the ReadWriteLock for writing then multiple thread can access the read lock.
    • Write Lock − If no thread is reading or writing, then one thread can access the write lock.

    Lock Methods

    Following is the list of important methods available in the Lock class.

    Sr.No.Method & Description
    1public Lock readLock()Returns the lock used for reading.
    2public Lock writeLock()Returns the lock used for writing.

    Example

    The following TestThread program demonstrates these methods of the ReadWriteLock interface. Here we’ve used readlock() to acquire the read-lock and writeLock() to acquire the write-lock.

    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class TestThread {
       private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
       private static String message = "a";
    
       public static void main(String[] args) throws InterruptedException {
    
      Thread t1 = new Thread(new WriterA());
      t1.setName("Writer A");
      
      Thread t2 = new Thread(new WriterB());
      t2.setName("Writer B");
      
      Thread t3 = new Thread(new Reader());
      t3.setName("Reader");
      t1.start();
      t2.start();
      t3.start();
      t1.join();
      t2.join();
      t3.join();
    } static class Reader implements Runnable {
      public void run() {
         
         if(lock.isWriteLocked()) {
            System.out.println("Write Lock Present.");
         }
         lock.readLock().lock();
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            System.out.println(Thread.currentThread().getName() +": "+ message );
            lock.readLock().unlock();
         }
      }
    } static class WriterA implements Runnable {
      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("a");
            lock.writeLock().unlock();
         }
      }
    } static class WriterB implements Runnable {
      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("b");
            lock.writeLock().unlock();
         }
      }
    } }

    This will produce the following result.

    Output

    Writer A  Time Taken 6 seconds.
    Write Lock Present.
    Writer B  Time Taken 2 seconds.
    Reader  Time Taken 0 seconds.
    Reader: aab
    
  • Lock Interface

    A java.util.concurrent.locks.Lock interface is used to as a thread synchronization mechanism similar to synchronized blocks. New Locking mechanism is more flexible and provides more options than a synchronized block. Main differences between a Lock and a synchronized block are following −

    • Guarantee of sequence − Synchronized block does not provide any guarantee of sequence in which waiting thread will be given access. Lock interface handles it.
    • No timeout − Synchronized block has no option of timeout if lock is not granted. Lock interface provides such option.
    • Single method − Synchronized block must be fully contained within a single method whereas a lock interface’s methods lock() and unlock() can be called in different methods.

    Lock Methods

    Following is the list of important methods available in the Lock class.

    Sr.No.Method & Description
    1public void lock()Acquires the lock.
    2public void lockInterruptibly()Acquires the lock unless the current thread is interrupted.
    3public Condition newCondition()Returns a new Condition instance that is bound to this Lock instance.
    4public boolean tryLock()Acquires the lock only if it is free at the time of invocation.
    5public boolean tryLock(long time, TimeUnit unit)Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted.
    6public void unlock()Releases the lock.

    Example

    The following TestThread program demonstrates some of these methods of the Lock interface. Here we’ve used lock() to acquire the lock and unlock() to release the lock.

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class PrintDemo {
       private final Lock queueLock = new ReentrantLock();
    
       public void print() {
    
      queueLock.lock();
      try {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() 
            + "  Time Taken " + (duration / 1000) + " seconds.");
         Thread.sleep(duration);
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         System.out.printf(
            "%s printed the document successfully.\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
    } } class ThreadDemo extends Thread { PrintDemo printDemo; ThreadDemo(String name, PrintDemo printDemo) {
      super(name);
      this.printDemo = printDemo;
    } @Override public void run() {
      System.out.printf(
         "%s starts printing a document\n", Thread.currentThread().getName());
      printDemo.print();
    } } public class TestThread { public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();
      ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
      ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
      ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);
      t1.start();
      t2.start();
      t3.start();
      t4.start();
    } }

    This will produce the following result.

    Output

    Thread - 1  starts printing a document
    Thread - 4  starts printing a document
    Thread - 3  starts printing a document
    Thread - 2  starts printing a document
    Thread - 1   Time Taken 4 seconds.
    Thread - 1  printed the document successfully.
    Thread - 4   Time Taken 3 seconds.
    Thread - 4  printed the document successfully.
    Thread - 3   Time Taken 5 seconds.
    Thread - 3  printed the document successfully.
    Thread - 2   Time Taken 4 seconds.
    Thread - 2  printed the document successfully.
    

    We’ve use ReentrantLock class as an implementation of Lock interface here. ReentrantLock class allows a thread to lock a method even if it already have the lock on other method.

  • ThreadLocalRandom Class

    A java.util.concurrent.ThreadLocalRandom is a utility class introduced from jdk 1.7 onwards and is useful when multiple threads or ForkJoinTasks are required to generate random numbers. It improves performance and have less contention than Math.random() method.

    ThreadLocalRandom Methods

    Following is the list of important methods available in the ThreadLocalRandom class.

    Sr.No.Method & Description
    1public static ThreadLocalRandom current()Returns the current thread’s ThreadLocalRandom.
    2protected int next(int bits)Generates the next pseudorandom number.
    3public double nextDouble(double n)Returns a pseudorandom, uniformly distributed double value between 0 (inclusive) and the specified value (exclusive).
    4public double nextDouble(double least, double bound)Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound (exclusive).
    5public int nextInt(int least, int bound)Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound (exclusive).
    6public long nextLong(long n)Returns a pseudorandom, uniformly distributed value between 0 (inclusive) and the specified value (exclusive).
    7public long nextLong(long least, long bound)Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound (exclusive).
    8public void setSeed(long seed)Throws UnsupportedOperationException.

    Example

    The following TestThread program demonstrates some of these methods of the Lock interface. Here we’ve used lock() to acquire the lock and unlock() to release the lock.

    import java.util.Random;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.ThreadLocalRandom;
    
    public class TestThread {
      
       public static void main(final String[] arguments) {
    
      System.out.println("Random Integer: " + new Random().nextInt());  
      System.out.println("Seeded Random Integer: " + new Random(15).nextInt());  
      System.out.println(
         "Thread Local Random Integer: " + ThreadLocalRandom.current().nextInt());
      
      final ThreadLocalRandom random = ThreadLocalRandom.current();  
      random.setSeed(15); //exception will come as seeding is not allowed in ThreadLocalRandom.
      System.out.println("Seeded Thread Local Random Integer: " + random.nextInt());  
    } }

    This will produce the following result.

    Output

    Random Integer: 1566889198
    Seeded Random Integer: -1159716814
    Thread Local Random Integer: 358693993
    Exception in thread "main" java.lang.UnsupportedOperationException
    
        at java.util.concurrent.ThreadLocalRandom.setSeed(Unknown Source)
        at TestThread.main(TestThread.java:21)

    Here we’ve used ThreadLocalRandom and Random classes to get random numbers.

  • ThreadLocal Class

    The ThreadLocal class is used to create thread local variables which can only be read and written by the same thread. For example, if two threads are accessing code having reference to same threadLocal variable then each thread will not see any modification to threadLocal variable done by other thread.

    ThreadLocal Methods

    Following is the list of important methods available in the ThreadLocal class.

    Sr.No.Method & Description
    1public T get()Returns the value in the current thread’s copy of this thread-local variable.
    2protected T initialValue()Returns the current thread’s “initial value” for this thread-local variable.
    3public void remove()Removes the current thread’s value for this thread-local variable.
    4public void set(T value)Sets the current thread’s copy of this thread-local variable to the specified value.

    Example

    The following TestThread program demonstrates some of these methods of the ThreadLocal class. Here we’ve used two counter variable, one is normal variable and another one is ThreadLocal.

    class RunnableDemo implements Runnable {
       int counter;
       ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>();
    
       public void run() {     
    
      counter++;
      if(threadLocalCounter.get() != null) {
         threadLocalCounter.set(threadLocalCounter.get().intValue() + 1);
      } else {
         threadLocalCounter.set(0);
      }
      System.out.println("Counter: " + counter);
      System.out.println("threadLocalCounter: " + threadLocalCounter.get());
    } } public class TestThread { public static void main(String args[]) {
      RunnableDemo commonInstance = new RunnableDemo();
      Thread t1 = new Thread(commonInstance);
      Thread t2 = new Thread(commonInstance);
      Thread t3 = new Thread(commonInstance);
      Thread t4 = new Thread(commonInstance);
      t1.start();
      t2.start();
      t3.start();
      t4.start();
      // wait for threads to end
      try {
         t1.join();
         t2.join();
         t3.join();
         t4.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
    } }

    This will produce the following result.

    Output

    Counter: 1
    threadLocalCounter: 0
    Counter: 2
    threadLocalCounter: 0
    Counter: 3
    threadLocalCounter: 0
    Counter: 4
    threadLocalCounter: 0
    

    You can see the value of counter is increased by each thread, but threadLocalCounter remains 0 for each thread.

  • 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. A Java multithreaded program may suffer from the deadlock condition because the synchronized keyword causes the executing thread to block while waiting for the lock, or monitor, associated with the specified object. Here is an example.

    Example

    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();
      ThreadDemo2 T2 = new ThreadDemo2();
      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 &amp; 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 &amp; 2...");
            }
         }
      }
    } }

    When you compile and execute the above program, you find a deadlock situation and following is the output produced by the program −

    Output

    Thread 1: Holding lock 1...
    Thread 2: Holding lock 2...
    Thread 1: Waiting for lock 2...
    Thread 2: Waiting for lock 1...
    

    The above program will hang forever because neither of the threads in position to proceed and waiting for each other to release the lock, so you can come out of the program by pressing CTRL+C.

    Deadlock Solution Example

    Let’s change the order of the lock and run of the same program to see if both the threads still wait for each other −

    Example

    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();
      ThreadDemo2 T2 = new ThreadDemo2();
      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 &amp; 2...");
            }
         }
      }
    } private static class ThreadDemo2 extends Thread {
      
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");
            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 &amp; 2...");
            }
         }
      }
    } }

    So just changing the order of the locks prevent the program in going into a deadlock situation and completes with the following result −

    Output

    Thread 1: Holding lock 1...
    Thread 1: Waiting for lock 2...
    Thread 1: Holding lock 1 & 2...
    Thread 2: Holding lock 1...
    Thread 2: Waiting for lock 2...
    Thread 2: Holding lock 1 & 2...
    

    The above example is to just make the concept clear, however, it is a complex concept and you should deep dive into it before you develop your applications to deal with deadlock situations.

  • Synchronization

    Multithreading Example with Synchronization

    Here is the same example which prints counter value in sequence and every time we run it, it produces the same result.

    Example

    class PrintDemo {
       
       public void printCount() {
    
      
      try {
         
         for(int i = 5; i &gt; 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
    } } class ThreadDemo extends Thread { private Thread t; private String threadName; PrintDemo PD; ThreadDemo(String name, PrintDemo pd) {
      threadName = name;
      PD = pd;
    } public void run() {
      
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
    } public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
    } } public class TestThread { public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();
      ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD);
      T1.start();
      T2.start();
      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
    } }

    This produces the same result every time you run this program −

    Output

    Starting Thread - 1
    Starting Thread - 2
    Counter   ---   5
    Counter   ---   4
    Counter   ---   3
    Counter   ---   2
    Counter   ---   1
    Thread Thread - 1  exiting.
    Counter   ---   5
    Counter   ---   4
    Counter   ---   3
    Counter   ---   2
    Counter   ---   1
    Thread Thread - 2  exiting.
    
  • Interthread Communication

    If you are aware of interprocess communication then it will be easy for you to understand interthread communication. Interthread communication is important when you develop an application where two or more threads exchange some information.

    There are three simple methods and a little trick which makes thread communication possible. All the three methods are listed below −

    Sr.No.Method & Description
    1public void wait()Causes the current thread to wait until another thread invokes the notify().
    2public void notify()Wakes up a single thread that is waiting on this object’s monitor.
    3public void notifyAll()Wakes up all the threads that called wait( ) on the same object.

    These methods have been implemented as final methods in Object, so they are available in all the classes. All three methods can be called only from within a synchronized context.

    Example

    This examples shows how two threads can communicate using wait() and notify() method. You can create a complex system using the same concept.

    class Chat {
       boolean flag = false;
    
       public synchronized void Question(String msg) {
    
    
      if (flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
    } public synchronized void Answer(String msg) {
      if (!flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = false;
      notify();
    } } class T1 implements Runnable { Chat m; String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" }; public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
    } public void run() {
      for (int i = 0; i &lt; s1.length; i++) {
         m.Question(s1&#91;i]);
      }
    } } class T2 implements Runnable { Chat m; String[] s2 = { "Hi", "I am good, what about you?", "Great!" }; public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
    } public void run() {
      for (int i = 0; i &lt; s2.length; i++) {
         m.Answer(s2&#91;i]);
      }
    } } public class TestThread { public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
    } }

    When the above program is complied and executed, it produces the following result −

    Output

    Hi
    Hi
    How are you ?
    I am good, what about you?
    I am also doing fine!
    Great!
    

    Above example has been taken and then modified from [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]

  • Major Operations

    Core Java provides complete control over multithreaded program. You can develop a multithreaded program which can be suspended, resumed, or stopped completely based on your requirements. There are various static methods which you can use on thread objects to control their behavior. Following table lists down those methods −

    Sr.No.Method & Description
    1public void suspend()This method puts a thread in the suspended state and can be resumed using resume() method.
    2public void stop()This method stops a thread completely.
    3public void resume()This method resumes a thread, which was suspended using suspend() method.
    4public void wait()Causes the current thread to wait until another thread invokes the notify().
    5public void notify()Wakes up a single thread that is waiting on this object’s monitor.

    Be aware that the latest versions of Java has deprecated the usage of suspend( ), resume( ), and stop( ) methods and so you need to use available alternatives.

    Example

    class RunnableDemo implements Runnable {
       public Thread t;
       private String threadName;
       boolean suspended = false;
    
       RunnableDemo(String name) {
    
      threadName = name;
      System.out.println("Creating " +  threadName );
    } public void run() {
      System.out.println("Running " +  threadName );
      try {
         
         for(int i = 10; i &gt; 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(300);
            synchronized(this) {
               
               while(suspended) {
                  wait();
               }
            }
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
    } public void start () {
      System.out.println("Starting " +  threadName );
      
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
    } void suspend() {
      suspended = true;
    } synchronized void resume() {
      suspended = false;
      notify();
    } } public class TestThread { public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo("Thread-1");
      R1.start();
      RunnableDemo R2 = new RunnableDemo("Thread-2");
      R2.start();
      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      } try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
    } }

    The above program produces the following output −

    Output

    Creating Thread-1
    Starting Thread-1
    Creating Thread-2
    Starting Thread-2
    Running Thread-1
    Thread: Thread-1, 10
    Running Thread-2
    Thread: Thread-2, 10
    Thread: Thread-1, 9
    Thread: Thread-2, 9
    Thread: Thread-1, 8
    Thread: Thread-2, 8
    Thread: Thread-1, 7
    Thread: Thread-2, 7
    Suspending First Thread
    Thread: Thread-2, 6
    Thread: Thread-2, 5
    Thread: Thread-2, 4
    Resuming First Thread
    Suspending thread Two
    Thread: Thread-1, 6
    Thread: Thread-1, 5
    Thread: Thread-1, 4
    Thread: Thread-1, 3
    Resuming thread Two
    Thread: Thread-2, 3
    Waiting for threads to finish.
    Thread: Thread-1, 2
    Thread: Thread-2, 2
    Thread: Thread-1, 1
    Thread: Thread-2, 1
    Thread Thread-1 exiting.
    Thread Thread-2 exiting.
    Main thread exiting.
    
  • Environment Setup

    In this chapter, we will discuss on the different aspects of setting up a congenial environment for Java.

    Local Environment Setup

    If you are still willing to set up your environment for Java programming language, then this section guides you on how to download and set up Java on your machine. Following are the steps to set up the environment.

    Java SE is freely available from the link Download Java. You can download a version based on your operating system.

    Follow the instructions to download Java and run the .exe to install Java on your machine. Once you installed Java on your machine, you will need to set environment variables to point to correct installation directories −

    Setting Up the Path for Windows

    Assuming you have installed Java in c:\Program Files\java\jdk directory −

    • Right-click on ‘My Computer’ and select ‘Properties’.
    • Click the ‘Environment variables’ button under the ‘Advanced’ tab.
    • Now, alter the ‘Path’ variable so that it also contains the path to the Java executable. Example, if the path is currently set to ‘C:\WINDOWS\SYSTEM32’, then change your path to read ‘C:\WINDOWS\SYSTEM32;c:\Program Files\java\jdk\bin’.

    Setting Up the Path for Linux, UNIX, Solaris, FreeBSD

    Environment variable PATH should be set to point to where the Java binaries have been installed. Refer to your shell documentation, if you have trouble doing this.

    Example, if you use bash as your shell, then you would add the following line to the end of your ‘.bashrc: export PATH = /path/to/java:$PATH’

    Popular Java Editors

    To write your Java programs, you will need a text editor. There are even more sophisticated IDEs available in the market. But for now, you can consider one of the following −

    • Notepad − On Windows machine, you can use any simple text editor like Notepad (Recommended for this tutorial), TextPad.
    • Netbeans − A Java IDE that is open-source and free which can be downloaded from https://netbeans.org/index.html.
    • Eclipse − A Java IDE developed by the eclipse open-source community and can be downloaded from https://www.eclipse.org/.