Unit 4
Unit 4
Unit 4
UNDERSTANDING THREADS
INTRODUCTION:
Multitasking:
Multitasking is a process of executing multiple tasks simultaneously.
Multitasking is when multiple processes share common processing resources such as a CPU.
Multitasking can be achieved in two ways:
Process-based Multitasking(Multiprocessing)
Thread-based Multitasking (Multithreading)
Process-based Multitasking (Multiprocessing)
Each process has its own address in memory i.e. each process allocates separate memory area.
Process is heavyweight.
Cost of communication between the processes is high.
Switching from one process to another require some time for saving and loading registers, memory
maps, updating lists etc.
Thread-based Multitasking (Multithreading)
Threads share the same address space.
Thread is lightweight.
Cost of communication between the thread is low.
Java Multithreading is mostly used in games, animation, etc.
THREAD LIFECYCLE
The term Threads in Java is a lightweight process that allows multiple tasks to be executed
simultaneously within a single program, thus improving the system’s performance.
The life cycle of the thread in java is controlled by JVM.
The java thread states are as follows:
New Born
Runnable
Running
Non-Runnable (Blocked/Waiting/sleep)
Terminated/Dead
New Born:
The thread enters the new born state as soon as it is created. At new born state, the thread is
considered not alive/Inactive.
The thread is created using the new operator.
Classname threadname = new classname(); Newborn state
From the new born state the thread can go to ready to run mode or dead state.
If start( ) method is called then the thread goes to ready to run mode. If the stop( ) method is called
then the thread goes to dead state.
Ready to run mode (Runnable Mode)
A thread first enters the runnable state when the start() method is invoked, but a thread can also return
to the runnable state after either running or coming back from a blocked, waiting, or sleeping state.
When the thread is in the runnable state, it is considered alive/active.
In the runnable environment, the thread is ready for execution and is awaiting the processor's
availability (CPU time). That is, the thread has entered the queue (line) of threads waiting for
execution.
The process of allotting time for the threads is called time slicing.
Running:
The processor (CPU) has assigned a time slot to the thread for execution.
A thread keeps running until the following condition occurs
The thread can also be forced to give up the control when one of the following conditions arise
A thread can be suspended by suspend( ) method. A suspended thread can be revived by using the
resume() method.
A thread can be made to sleep for a particular time by using the sleep(milliseconds) method. The
sleeping method re-enters runnable state when the time elapses.
A thread can be made to wait until a particular event occur using the wait() method, which can be run
again using the notify( ) method.
A thread is pre-empted by a higher priority thread
Waiting/blocked/sleeping:
This is the state when the thread is still alive, but is currently not eligible to run.
Here, three states combined into one, but they all have one thing in common: the thread is still alive,
but is currently not eligible to run.
In other words, it is not runnable, but it might return to a runnable state later if a particular event occurs.
A thread may be blocked waiting for a resource (like I/O or an object's lock), in which case the event
that sends it back to runnable is the availability of the resource.
A thread may be sleeping because the thread's run code tells it to sleep for some period of time, in which
case the event that sends it back to runnable is that it wakes up because its sleep time has expired.
A thread may be waiting, because the thread’s run code causes it to wait, in which case the event that
sends it back to runnable is that another thread sends a notification that it may no longer be necessary
for the thread to wait.
Dead
A thread will be in Terminated State, due to the below reasons:
Termination is achieved by a Thread when it finishes its task Normally.
Sometimes Threads may be terminated due to unusual events like segmentation faults,
exceptions…etc. and such kind of Termination can be called Abnormal Termination.
A terminated Thread means it is dead and no longer available.
Once a thread is dead, it can never be brought back to life.
If you invoke start() on a dead Thread instance, you'll get a runtime (not compiler) exception.
Note:
Waiting
If there are two threads, T1 and T2 where T1 needs to communicate to the camera and the other thread T2
already using a camera to scan then T1 waits until T2 Thread completes its work, at this state T1 is parked in
waiting for the state.
Blocked State
The user called two Threads T2 and T3 with the same functionality and both had same time slice given by
Thread Scheduler then both Threads T1, T2 is in a blocked state.
Sleep
There are two threads T1, T2 waiting for CPU and T1 is undergoing a Critical Coding operation and if it does
not exist the CPU until its operation gets executed then T2 will be exposed to longer waiting with undetermined
certainty, In order to avoid this starvation situation, we had Timed Waiting for the state to avoid that kind of
scenario as in Timed Waiting, each thread has a time period for which sleep() method is invoked and after the
time expires the Threads starts executing its task.
Example:
import java.io.*;
import java.util.*;
Runnable interface:
The Runnable interface should be implemented by any class whose instances are intended to be executed
by a thread.
Runnable interface have only one method named run().
public void run(): is used to perform action for a thread.
steps to create the thread using Runnable interface
Create any user defined class and implements runnable interface
class class_Name implements Runnable
{
........
}
Override run() method within the user defined class.
Create an object for user-defined thread class and attached that object to predefined thread class.
Class_Name obj=new Class_Name ( );
Thread t=new Thread(obj )
Call start() method of thread class to execute run() method.
t.start( );
Save the program with filename.java
Example:
class Multi3 implements Runnable
{
public void run()
{
System.out.println("thread is running...");
}
public static void main(String args[])
{
Multi3 m1=new Multi3();
Thread t1 =new Thread(m1);
t1.start();
}
}
Output
yield( ):
It make the currently running thread head back to runnable to allow other threads of the same priority
to get their turn.
A yield() won't go to the waiting/sleeping/ blocking state.
join():
one thread "join onto the end “of another thread.
If you have a thread_B that can't do its work until another thread_A has completed its work, then you
want thread_B to "join" thread_A. This means that thread_B will not become runnable until thread_A
has finished (and entered the dead state).
Thread t = new Thread();
t.start();
t.join();
SYNCHRONIZING THREADS
When multiple threads can access the same resource can produce corrupted data or inconsistent data.
At a time when more than one thread try to access a shared resource, we need to ensure that resource
will be used by only one thread at a time. The process by which this is achieved is called
synchronization. T
Synchronization is the process by which it ensure that the shared resource will be used by only one
thread at a time. This can be achieved using synchronized keyword.
While a thread is inside a synchronized method, all other threads that try to call it (or any other
synchronized method) on the same instance have to wait.
It reduces the level of concurrency.
Why use Synchronization
The synchronization is mainly used to
1. To prevent thread interference.
2. To prevent consistency problem.
Thread Synchronization
There are two types of thread synchronization mutual exclusive and inter-thread communication.
1. Mutual Exclusive
1. synchronized method.
2. synchronized block.
3. static synchronization.
2. Cooperation (Inter-thread communication in java)
Mutual Exclusive:
The objective of mutual exclusion is to maintain data integrity and preserve the consistency of shared
resources in multi-threaded or multi-process environments.
When multiple processes or threads attempt to access the same resource simultaneously, there is a risk
of conflicting operations leading to incorrect results or unpredictable behavior.
Mutual exclusion provides a mechanism to control access to the shared resource, ensuring that only one
entity can operate on it at a time.
To achieve mutual exclusion, synchronization mechanisms like locks, semaphores, and critical sections are
employed.
1. Synchronized Methods
syntax:
accessspecifier synchronozied return-type method-name(list of arg)
{
}
if a thread invokes a synchronized method on an object , the lock of that object is first acquired , the method
body executed and then the lock released. Another thread invoking synchronized method on that same object
will block until the lock is released. Lock is released as a soon as the method terminates.
Example program:
class SyncDemo implements Runnable
{
public void run()
{
display();
synchronized void display() // synchronized method can aceesed by only one thread at a time
{
for (int x = 1; x < 5; x++)
{
System.out.println("Run by "+ Thread.currentThread().getName());
try
{
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
System.out.println("Exception Caught" + ex);
}
}
}
public static void main (String [] args)
{
// Make one Runnable
SyncDemo nr = new SyncDemo();
Thread one = new Thread(nr,"Thread-A");
Thread two = new Thread(nr,"Thread-B");
Thread three = new Thread(nr,"Thread-C");
//Start the thread
one.start();
two.start();
three.start();
}
}
Output :
Note : Notice the output , As Synchronized method allow only one thread to be executed at a time , all the
three threads are executed serially, that is one after another , not concurrently.
Example program:
class SyncDemo implements Runnable
{
public void run()
{
synchronized(this)
{
for (int x = 1; x < 5; x++)
{
System.out.println("Run by "+ Thread.currentThread().getName());
try
{
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
System.out.println("Exception Caught" + ex);
}
}
}
}
}
class Main
{
public static void main (String [] args)
{
// Make one Runnable
SyncDemo nr = new SyncDemo();
Thread one = new Thread(nr,"Thread-A");
Thread two = new Thread(nr,"Thread-B");
Thread three = new Thread(nr,"Thread-C");
Output:
3. static synchronization.
If you make any static method as synchronized, the lock will be on the class not onobject.
Use synchronized keyword on the static method to perform static synchronization.
Syntax:
synchronozied static return-type method-name(list of arg)
{
---------------
}
Example:
class SyncDemo implements Runnable
{
public void run()
{
SyncDemo.display();
synchronized static void display() // synchronized method can aceesed by only one thread at a time
{
for (int x = 1; x < 5; x++)
{
System.out.println("Run by "+ Thread.currentThread().getName());
try
{
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
System.out.println("Exception Caught" + ex);
}
}
}
public static void main (String [] args)
{
// Make one Runnable
SyncDemo nr = new SyncDemo();
Thread one = new Thread(nr,"Thread-A");
Thread two = new Thread(nr,"Thread-B");
Thread three = new Thread(nr,"Thread-C");
//Start the thread
one.start();
two.start();
three.start();
}
}
Output:
INTER-THREAD COMMUNICATION
Inter-thread communication is important when you develop an application where two or more threads
communicate to each other in order to exchange some information.
How to Achieve Inter Thread Communication in Java
Inter-thread communication is achieved by using the wait(), notify(), and notifyAll() methods of
the Object class.
Methods used for Inter-thread Communication
There are three simple methods and a little trick which makes thread communication possible. All the
three methods are listed below −
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.
Drawback
Scenario-1: producer adding items in a queue but consumer not taking items
Solution: While implementing producer consumer problem , above two drawbacks have to solve, first chance
will be given to the producer(producer or consumer), producer have to insert only one item in a queue, until
consumer consumes the item, producer cannot add the second item in a queue
Example program:
class Buffer
{
private int contents;
private boolean available = false;
Output:
THREAD PRIORITIES
Thread priority in Java is a number assigned to a thread that is used by Thread scheduler to decide
which thread should be allowed to execute.
In other words, thread priority is a feature in Java that helps the thread scheduler to determine the order
in which threads are executed.
Each thread is assigned a different priority in Java that will decide the order (preference) in which it is
scheduled for running.
Thread priorities are represented by a number from 1 to 10 that specifies the relative priority of one
thread to another. The thread with the highest priority is selected by the scheduler to be executed first.
Thread class in Java provides three priority constants to define the priority of a thread. These are:
1) Thread.MIN_PRIORITY = 1
2) Thread.NORM_PRIORITY = 5
3) Thread.MAX_PRIORITY = 10
The Thread.MIN_PRIORITY represents the minimum priority level for a thread. Its value is 1.
The Thread.NORM_PRIORITY represents the normal priority level for a thread. Its value is 5.
The Thread.MAX_PRIORITY represents the maximum priority for a thread. Its value is 10.
All these priority constants are public, final, and static members of the Thread class.
The threads having equal priorities share the processor time on the first-come, first-serve basis.
If any high priority thread enters into the runnable state, it will preempt the currently running thread
forcing it to move to the runnable state. Note that the highest priority thread always preempts any lower
priority thread.
How to Get Priority of Current Thread in Java?
syntax
ThreadName.getPriority();
Here, ThreadName represents the object reference variable of the Thread class.
Example 1:
public class A implements Runnable {
public void run()
{
System.out.println(Thread.currentThread()); // This method is static.
}
public static void main(String[] args)
{
A a = new A();
Thread t = new Thread(a, "NewThread");
In this example,
How to Set Priority of Thread in Java?
The setPriority() accepts an integer value as an argument and sets that value as priority of a thread
through which it is called.
syntax
ThreadName.setPriority(n); // Here, n is an integer value which ranges from 1 to 10.
Example:
public class A implements Runnable {
public void run()
{
System.out.println(Thread.currentThread()); // This method is static.
}
public static void main(String[] args)
{
A a = new A();
Thread t = new Thread(a, "NewThread");
t.setPriority(2); // Setting the priority of thread.
Output:
Example:
In this example, we are setting priority of two thread and running them to see the effect of thread priority. Does
setting higher priority thread get CPU first. See the below example.
Program:
class MyThread extends Thread
{
public void run()
{
System.out.println("Thread Running... "+Thread.currentThread().getName());
}
public static void main(String[]args)
{
MyThread p1 = new MyThread();
MyThread p2 = new MyThread();
// Starting thread
p1.start();
p2.start();
// Setting priority
p1.setPriority(2);
// Getting -priority
p2.setPriority(1);
int p = p1.getPriority();
int p22 = p2.getPriority();
}
}
Output:
Note: Thread priorities cannot guarantee that a higher priority thread will always be executed first than the
lower priority thread. The selection of the threads for execution depends upon the thread scheduler which is
platform dependent.
GENERIC PROGRAMMING
Generic programming is important feature that were added to the Java 5 in 2004.
With the help of generic programming concept developers can create programs that can handle many
data types.
The term generics means parameterized types.
The type parameters naming conventions are important to learn generics thoroughly. The common
type parameters are as follows:
T - Type
E - Element
K - Key
N - Number
V - Value
Parameterized types enable you to create a single class, interface, and method that can be used with
different types of data (objects).
It is an effective strategy that enhances the readability, maintainability, and reusability of code.
Note: Generics does not work with primitive types (int, float, char, etc).
Advantage of Java Generics
Type Safety: The ability to create type-safe code in which type-mismatch errors are caught at compile
time is a key advantage of generics
Reusability: Generic programming enables code to be written once and used for a variety of data types
Improved Readability: Generics improve readability by making it explicit which data types a class or
method is meant to operate on. Developers will have an easier time understanding the code as a result,
which lowers the risk of mistakes.
Types of Java Generics
Generic Methods
Generic Classes
GENERIC METHODS
Method that uses parameterized types are called Generic Method, Create a method that can be
used with any type of data. Such a method is known as Generics Method.
It is possible to declare a generic method that uses one or more type parameters.
It is also possible to create a generic method that is enclosed within a non-generic class.
Syntax: Declaring Generic method
<parameter_type> return_type method_name (parameter_type parameters_name)
{
...
}
How to call generic method
Objectname.<wrapper class>methodname(arguments);
Or
Objectnme.methodname(arguments);
Example1:Passing single parmeter
class DemoClass
{
// creae a generics method
<T> void genericsMethod(T data)
{
System.out.println("Generics Method:");
System.out.println("Data Passed: " + data);
}
}
class GenericMethod
{
public static void main(String[] args)
{
DemoClass demo = new DemoClass();
Output:
Note:
We can call the generics method without including the type parameter.
For example, demo.genericsMethod("Java Programming");
In this case, the compiler can match the parameter type based on the value passed to the method.
Output:
Example3: Passing multiple parmeters of different type
class DemoClass
{
// creae a generics method
<T1,T2> void genericsMethod(T1 data1, T2 data2)
{
System.out.println("Generics Method:");
System.out.println("Data Passed: " + data1);
System.out.println("Data Passed: " + data2);
}
}
class GenericMethod
{
public static void main(String[] args)
{
Output:
GENERIC CLASSES
A class that can refer to any type is known as a generic class.
Here, we are using the T type parameter to create the generic class of specific type.
A generic class is defined just like a normal class, with the addition of a list of type parameters in angle
brackets <...> after the class name.
The type parameters can then be used throughout the class definition to declare variables and specify
parameter types for methods.
The type parameter section of a generic class can have one or more type parameters separated by
commas. These classes are known as parameterized classes or parameterized types because they accept
one or more parameters.
class Box<T>
{
T a;
Box(T t)
{
a = t;
}
void print()
{
System.out.println("Value="+a);
}
}
class Main
{
public static void main(String[] args)
{
// Create a Gen reference for Integers.
Box<Integer> b1 = new Box<Integer>(10);
// Create a Gen reference for string.
Box<String> b2= new Box<String>(“Hello”);
b1.print();
b2.print();
}
}
Output:
BOUNDED TYPES
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the
bounded type parameter.
If you just specify a type (class) as bounded parameter, only sub types of that particular class are
accepted by the current generic class. These are known as bounded-types in generics in Java.
Example
In the following Java example the generic class Sample restricts the type parameter to the sub classes of
the Number classes using the bounded parameter.
class Sample <T extends Number>
{
T data;
Sample(T data){
this.data = data;
}
public void display()
{
System.out.println("Data value is: "+data);
}
}
public class BoundsExample
{
public static void main(String args[])
{
Sample<Integer> obj1 = new Sample<Integer>(20);
obj1.display();
Sample<Double> obj2 = new Sample<Double>(20.22d);
obj2.display();
Sample<Float> obj3 = new Sample<Float>(125.332f);
obj3.display();
}
}
Output
Now, if you pass other types as parameters to this class (say, String for example) a compile time error will be
generated.
Example
public class BoundsExample {
public static void main(String args[]) {
Sample<Integer> obj1 = new Sample<Integer>(20);
obj1.display();
Sample<Double> obj2 = new Sample<Double>(20.22d);
obj2.display();
Sample<String> obj3 = new Sample<String>("Krishna");
obj3.display();
}
}
INTRODUCTION TO JDBC
JDBC stands for Java Database Connectivity.
JDBC is a Java API (Application Programming Interface) to connect and execute the query with the
database.
Before JDBC, ODBC API was the database API to connect and execute the query with the database.
But, ODBC API uses ODBC driver which is written in C language (i.e. platform dependent and
unsecured). That is why Java has defined its own API (JDBC API) that uses JDBC drivers (written in
Java language).
JDBC is platform independent
We can use JDBC API to handle database using Java program and can perform the following
activities:
Connect to the database
Execute queries and update statements to the database
Retrieve the result received from the database.
JDBC is used to interact with various type of Database such as Oracle, MS Access, My SQL and SQL
Server.
The JDBC classes are contained in the Java Package java.sql and javax.sql.
The JDBC API supports both two-tier and three-tier processing models for database access but in
general, JDBC Architecture consists of two layers.
Two-tier model:
A java application communicates directly to the data source.
The JDBC driver enables the communication between the application and the data source.
When a user sends a query to the data source, the answers for those queries are sent back to the user in
the form of results.
The data source can be located on a different machine on a network to which a user is connected. This
is known as a client/server configuration, where the user’s machine acts as a client, and the machine
has the data source running acts as the server.
Suitable for small applications with limited users, where database operations can be managed within
the client.
Advantage
It is easy to maintain and modification also easy
Communication is faster.
Disadvantage
Application performance will be degrade based on increasing users
Three-tier model:
In this, the user’s queries are sent to middle-tier services, from which the commands are again sent to
the data source. The results are sent back to the middle tier, and from there to the user.
JDBC components
The JDBC core comes with the following interfaces and classes:
Driver: This is the interface that controls communication with the database server. It also withdraws
information associated with driver objects.
Driver Manager: It manages any required set of JDBC drivers
Connection: This is an interface or session that houses all the methods to connect to any database.
Statements: This is used to carry out a static SQL statement
ResultSet: This is used to access the result row-by-row
JDBC DRIVERS AND ARCHITECTURE
JDBC ARCHITECTURE
The architecture of JDBC follows a layered approach, consisting of three main layers: JDBC API,
JDBC Driver Manager, JDBC Drivers.
Java Application
It is a java applet or a servlet that communicates with a data source.
JDBC API
This layer provides a set of classes and interfaces that define the API for Java applications to interact
with databases.
It includes classes for establishing connections, executing queries, and processing results.
JDBC Driver Manager
The Driver Manager is responsible for loading and managing the JDBC drivers.
It acts as a central component for managing the available drivers and creating connections to the
database.
JDBC Drivers
To communicate with a data source through JDBC, you need a JDBC driver that intelligently
communicates with the respective data source.
Advantages
Easy to use and maintain.
We can access any database.
It is available as part of JDK and hence, we are not required to install it separately.
Disadvantage
The performance is very less. Because, first it converts JDBC calls into ODBC calls and ODBC driver
converts ODBC calls into database specific calls
It is not suitable for large scale applications
Type 2 − JDBC-Native API
It is also known as native API partly java driver.
Type-2 driver is similar to type-1 driver except that ODBC driver is replaced with database vendor
specific native library.
Native libraries are set of functions written in non_java.
We have to install vendor provided native libraries on the client machine
Type-2 driver converts JDBC calls into vendor specific native library calls.
The native library calls can be understandable directly by database
Advantage
It provide better performance as compared to Type-1 driver because it required only one level
conversion from JDBC to native library calls.
Disadvantage
It is database dependent driver, because it internally uses database native libraries
Driver is platform dependent.
Native driver needs to be installed on client machine.
Advantage
No client side library is required.
This driver is server based, so no need for vendor database library to present on the client machine.
Disadvantage
Network support is required on client machine.
Database specific coding is done in middleware.
Maintenance of the middle tier becomes costly.
The Class.forName method is used to load the driver class, and the org.postgresql.Driver string specifies
the name of the driver class.
The dbUrl variable specifies the URL of the database, which includes the protocol (jdbc:postgresql),
the hostname (localhost), the port number (5432), and the database name (mydatabase).
The username and password variables specify the credentials to use for the connection.
The DriverManager.getConnection method is used to establish the connection, and it takes the database
URL, username, and password as parameters.
The resulting connection is stored in the conn variable.
This line creates a Statement object that can be used to execute SQL queries on the database.
The conn.createStatement method is used to create the statement, and it returns a new Statement
object.
The resulting statement is stored in the stmt variable.
These lines retrieve the results of the query and print them to the console.
The rs.next method is used to move to the next row in the result set, and it returns true if there is a next
row, or false if there are no more rows.
The while loop iterates over the rows in the result set, and for each row, it prints the value of the first
column (getString(1)) to the console using System.out.println.
Example:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;