UNIT 5 Notes.docx
UNIT 5 Notes.docx
UNIT 5 Notes.docx
Multithreaded Programming:
Multithreading is a Java feature that allows concurrent execution of two or more
parts of a program for maximum utilization of CPU. Each part of such program is
called a thread. So, threads are light-weight processes within a process.
Threads can be created by using two mechanisms :
1. Extending the Thread class
2. Implementing the Runnable Interface
We create a class that extends the java.lang.Thread class. This class overrides the
run() method available in the Thread class. A thread begins its life inside run()
method. We create an object of our new class and call start() method to start the
execution of a thread. Start() invokes the run() method on the Thread object.
Creating a Thread:
Thread Class
The simplest way to create a thread in Java is by extending the Thread class and
overriding its run() method. Thread class provide constructors and methods to
create and perform operations on a thread. Thread class extends Object class and
implements Runnable interface.
o Thread()
o Thread(String name)
o Thread(Runnable r)
o Thread(Runnable r, String name)
Starting a Thread
The start() method of the Thread class is used to start a newly created thread. It
performs the following tasks:
Thread Creation
1) Creating Thread by Extending Thread Class
thread is running...
Java Thread Example by implementing Runnable interface
thread is running...
Creating more than one thread to perform multiple tasks is called multithreading
in Java. In multiple threading programming, multiple threads are executing
simultaneously that improves the performance of CPU because CPU is not idle if
other threads are waiting to get some resources.
Multiple threads share the same address space in the heap memory. Therefore, it is
good to create multiple threads to execute multiple tasks rather than creating
multiple processes.
Example:
MyThread(String task)
{
this.task = task;
}
public void run()
{
for(int i = 1; i <= 5; i++)
{
System.out.println(task+ " : " +i);
try {
Thread.sleep(1000); // Pause the thread execution for 1000 milliseconds.
} catch(InterruptedException ie) {
System.out.println(ie.getMessage());
}
} // end of for loop.
} // end of run() method.
public static void main(String[] args)
{
MyThread th1 = new MyThread("Cut the ticket");
MyThread th2 = new MyThread("Show your seat number");
constructor of Thread class.
Thread t1 = new Thread(th1);
Thread t2 = new Thread(th2);
t1.start();
t2.start();
}
}
Output:
Cut the ticket : 1
Show your seat number : 1
Show your seat number : 2
Cut the ticket : 2
Show your seat number : 3
Cut the ticket : 3
Show your seat number : 4
Cut the ticket : 4
Show your seat number : 5
Cut the ticket : 5
The isAlive() method works in tandem with the join() method, which allows one
thread to wait for the completion of another. By invoking isAlive() on a target
thread and subsequently calling join(), a waiting thread can be sure that the target
thread has completed its execution before proceeding further.
While the isAlive() method provides valuable insights into the status of a thread,
it's important to note that it only provides a snapshot of the thread's status at the
time of invocation. The thread's status can change immediately after the isAlive()
method call, rendering the obtained result outdated.
// Create a thread
Thread thread = new Thread(() -> {
System.out.println("Thread started");
try {
Thread.sleep(2000); // Sleep for 2 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread finished");
});
Output:
Thread started
Is thread alive? true
Thread finished
Main thread continued
Thread Priorities:
import java.lang.*;
class GFG extends Thread {
public void run()
{
System.out.println("Inside run method");
}
public static void main(String[] args)
{
Thread.currentThread().setPriority(6);
System.out.println("main thread priority :
"+Thread.currentThread().getPriority());
GFG t1 = new GFG();
Synchronization:
Java provides a way of creating threads and synchronizing their tasks using
synchronized blocks.
A synchronized block in Java is synchronized on some object. All synchronized
blocks synchronize on the same object and can only have one thread executed
inside them at a time. All other threads attempting to enter the synchronized block
are blocked until the thread inside the synchronized block exits the block. If you
want to master concurrency and understand how to avoid common pitfalls.
Types of Synchronization
There are two synchronizations in Java mentioned below:
1. Process Synchronization
2. Thread Synchronization
1. Process Synchronization in Java
Process Synchronization is a technique used to coordinate the execution of multiple processes. It ensures
that the shared resources are safe and in order.
2. Thread Synchronization in Java
Thread Synchronization is used to coordinate and ordering of the execution of the threads in a
multi-threaded program. There are two types of thread synchronization are mentioned below:
● Mutual Exclusive
● Cooperation (Inter-thread communication in Java)
import java.io.*;
import java.util.*;
// A Class used to send a message
class Sender {
public void send(String msg)
{
System.out.println("Sending\t" + msg);
try {
Thread.sleep(1000);
}
catch (Exception e) {
System.out.println("Thread interrupted.");
}
System.out.println("\n" + msg + "Sent");
}
}
// Driver class
class SyncDemo {
public static void main(String args[])
{
Sender send = new Sender();
ThreadedSend S1 = new ThreadedSend(" Hi ", send);
ThreadedSend S2 = new ThreadedSend(" Bye ", send);
Hi Sent
Sending Bye
Bye Sent
Interthread Communication:
Inter-thread communication is important when you develop an application where two or more
threads exchange some information. Inter-thread communication is achieved by using
the wait(), notify(), and notifyAll() methods of the Object class.
Sr.No. Method & Description
public void wait()
1 Causes the current thread to wait until another thread invokes the
notify().
public void notify()
2
Wakes up a single thread that is waiting on this object's monitor.
public void notifyAll()
3
Wakes up all the threads that called wait( ) on the same object.
public class A {
int i;
boolean flag = false; // flag will be true when data production is over.
synchronized void deliver(int i)
{
if(flag)
try {
wait(); // Wait till a notification is received from Thread2. There will be no
wastage of time.
}
catch(InterruptedException ie) {
System.out.println(ie);
}
this.i = i;
flag = true; // When data production is over, it will store true into flag.
System.out.println("Data Delivered: " +i);
notify(); // When data production is over, it will notify Thread2 to use it.
}
synchronized int receive()
{
if(!flag)
try {
wait(); // Wait till a notification is received from Thread1.
}
catch(InterruptedException ie){
System.out.println(ie);
}
System.out.println("Data Received: " + I);
flag = false; // It will store false into flag when data is received.
notify(); // When data received is over, it will notify Thread1 to produce next
data.
return i;
}
}
public class Thread1 extends Thread
{
A obj;
Thread1(A obj)
{
this.obj = obj;
}
public void run()
{
for(int j = 1; j <= 5; j++){
obj.deliver(j);
}
}}
public class Thread2 extends Thread
{
A obj;
Thread2(A obj)
{
this.obj = obj;
}
public void run()
{
for(int k = 0; k <= 5; k++) {
obj.receive();
}
}
}
public class Communication {
public static void main(String[] args)
{
A obj = new A(); // Creating an object of class A.
// Creating two thread objects and pass reference variable obj as parameter to
Thread1 and Thread2.
Thread1 t1 = new Thread1(obj);
Thread2 t2 = new Thread2(obj);
// Run both threads.
t1.start();
t2.start();
}
}
Output
Output:
Data Delivered: 1
Data Received: 1
Data Delivered: 2
Data Received: 2
Data Delivered: 3
Data Received: 3
Data Delivered: 4
Data Received: 4
Data Delivered: 5
Data Received: 5
It is used to perform complicated tasks in the background without disturbing the main program. In order
to suspend the thread temporarily by making it go through from running state to waiting for the state. The
concept used in achieving the goal is the suspend() function.
Thread.stop() is being phased out due to its inherent risk. When you stop a thread, it unlocks all the
monitors it has locked. Other threads might see these objects in an inconsistent state if any of the objects
previously protected by these monitors were in an inconsistent state.
Threads acting on damaged objects can act erratically, whether consciously or unconsciously.
ThreadDeath, unlike other uncontrolled exceptions, silently kills threads, leaving the user with no warning
that the program may be corrupted. After the damage has occurred, the corruption may appear at an
unpredicted moment. Also, killing a thread will create a problem while working with DBMS – JDBC in a
multithreaded environment.
Thread.suspend() is deprecated because it is inherently deadlock-prone. As a
result, Thread.resume() must be deprecated as well. When the target thread is suspended, it holds a lock
on the monitor protecting a crucial system resource, and no other thread may access it until the target
thread is resumed. Deadlock occurs if the thread that would restart the target thread tries to lock this
monitor before invoking resume().
class NumVal {
private int num[] = null;
boolean valueSet = false;
int i = 0;
NumVal()
{
// Creating integer array of 10 elements
num = new int[10];
}
// method to set the values in the array
public void setVal(int n)
{
if (i < 9) {
System.out.println("Putting value " + n
+ " in the NumVal Array");
num[i] = n;
i++;
}
}
// method to get the values from the array
public int getVal()
{
if (i >= 0) {
A thread in Java at any point of time exists in any one of the following states. A thread lies only in one of
the shown states at any instant:
1. New State
2. Runnable State
3. Blocked State
4. Waiting State
5. Timed Waiting State
6. Terminated State
The diagram shown below represents various states of a thread at any instant in time.
● Because it exits normally. This happens when the code of the thread has been entirely executed by
the program.
● Because there occurred some unusual erroneous event, like a segmentation fault or an unhandled
exception.
System.out.println(
"State of thread1 while it called join() method on thread2 -"
+ Test.thread1.getState());
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
try {
// waiting for thread2 to die
thread2.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"State of thread2 when it has finished it's execution - "
+ thread2.getState());
}
}
Output
State of thread1 after creating it - NEW
State of thread1 after calling .start() method on it - RUNNABLE
State of thread2 after creating it - NEW
State of thread2 after calling .start() method on it - RUNNABLE
State of thread2 after calling .sleep() method on it - TIMED_WAITING
State of thread1 while it called join() method on thread2 -WAITING
State of thread2 when it has finished it's execution - TERMINATED
A Java enumeration is a class type. Although we don’t need to instantiate an enum using new, it has the
same capabilities as other classes. This fact makes Java enumeration a very powerful tool. Just like
classes, you can give them constructors, add instance variables and methods, and even implement
interfaces.
enum Level {
LOW,
MEDIUM,
HIGH
}
switch(myVar) {
case LOW:
System.out.println("Low level");
break;
case MEDIUM:
System.out.println("Medium level");
break;
case HIGH:
System.out.println("High level");
break;
}
}
}
Medium level
Type Wrappers:
A Wrapper class in Java is a class whose object wraps or contains primitive data types. When we create
an object to a wrapper class, it contains a field and in this field, we can store primitive data types. In other
words, we can wrap a primitive value into a wrapper class object.
import java.util.ArrayList;
class Main {
public static void main(String[] args) {
//autoboxing
list.add(5);
list.add(6);
RED,
GREEN,
Color c1 = Color.RED;
System.out.println(c1);
}
Output
RED
valueOf() method converts data from its internal form into a human-readable form. It is a static method
that is overloaded within a string for all of Java’s built-in types so that each type can be converted
properly into a string.
It is called when a string representation of some other type of data is needed for example during a
concatenation operation. You can call this method with any data type and get a reasonable String
representation valueOf() returns java.lang.Integer, which is the object representative of the integer.
System.out.println(sample);
1. They convert primitive data types into objects. Objects are needed if we wish to modify the arguments
passed into a method (because primitive types are passed by value).
2. The classes in java.util package handles only objects and hence wrapper classes help in this case also.
3. Data structures in the Collection framework, such as ArrayList and Vector, store only objects
(reference types) and not primitive types.
4. An object is needed to support synchronization in multithreading.
Unboxing on the other hand refers to converting an object of a wrapper type to its corresponding
primitive value. For example conversion of Integer to int. The Java compiler applies to unbox when an
object of a wrapper class is:
● Passed as a parameter to a method that expects a value of the corresponding primitive type.
● Assigned to a variable of the corresponding primitive type.
class BoxingExample1{
public static void main(String args[]){
int a=50;
Integer a2=new Integer(a);//Boxing
Integer a3=5;//Boxing
System.out.println(a2+" "+a3);
}
}
Output:50 5
class UnboxingExample1{
public static void main(String args[]){
Integer i=new Integer(50);
int a=i;
System.out.println(a);
}
}
Output:
50
********************************************* END***********************************