Unit 3
Unit 3
Exception Handling - Concepts of exception handling, exception hierarchy, usage of try, catch, throw,
throws and finally, built in exceptions, creating own exception subclasses.
Package java.util- The Collection Interfaces, The Collection classes: LinkedList Class, HashSet Class.
TreeSet Class, String Tokenizer, Date, Random, Scanner.
Multi-Threading: Differences between multithreading and multitasking, thread life cycle, creating threads,
thread priorities, synchronizing threads, inter thread communication.
System-generated exceptions are automatically thrown by the Java run-time system. To manually throw an exception,
use the keyword throw. Any exception that is thrown out of a method must be specified as such by a throws clause. Any
code that absolutely must be executed after a try block completes is put in a finally block.
This is the general form of an exception-handling block:
try {
// block of code to monitor for errors
}
catch (ExceptionType1 exOb) {
// exception handler for ExceptionType1
}
catch (ExceptionType2 exOb) {
// exception handler for ExceptionType2
}
// ...
finally {
// block of code to be executed after try block ends
} Here, ExceptionType is the type of exception that has occurred.
Exception Types:
All exception types are subclasses of the built-in class Throwable. Thus, Throwable is at the top of the exception class hierarchy.
Immediately below Throwable are two subclasses that partition exceptions into two distinct branches. One branch is headed by
Exception. This class is used for exceptional conditions that user programs should catch. This is also the class that you will subclass
to create your own custom exception types. There is an important subclass of Exception, called RuntimeException. Exceptions of
this type are automatically defined for the programs that you write and include things such as division by zero and invalid array
indexing.
The other branch is topped by Error, which defines exceptions that are not expected to be caught under normal circumstances by
your program. Exceptions of type Error are used by the Java run-time system to indicate errors having to do with the run-time
environment, itself. Stack overflow is an example of such an error.
Exception Hierarchy: Exceptions are recoverable, whereas Errors are non-recoverable. Throwable is a class (root
class for java exception handling)
Throwable (2 child classes)
}
O/P:
C:\JavaApplications>javac Exc3.java
C:\JavaApplications>java Exc3
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Exc3.doMore(Exc3.java:14)
at Exc3.doStuff(Exc3.java:9)
at Exc3.main(Exc3.java:5)
Using try and catch:
Although the default exception handler provided, you will usually want to handle an exception yourself. Doing so provides two
benefits. First, it allows you to fix the error. Second, it prevents the program from automatically terminating.
To guard against and handle a run-time error, simply enclose the code that you want to monitor inside a try block. Immediately
following the try block, include a catch clause that specifies the exception type that you wish to catch.
class Exc2 {
public static void main(String args[]) {
int d, a;
try { // monitor a block of code.
d = 0;
a = 42 / d;
System.out.println("This will not be printed.");
} catch (ArithmeticException e) { // catch divide-by-zero error
System.out.println("Division by zero.");
}
System.out.println("After catch statement.");
}
}
This program generates the following output:
Division by zero.
After catch statement.
Notice that the call to println( ) inside the try block is never executed. Once an exception is thrown, program control transfers out of
the try block into the catch block. Put differently, catch is not “called,” so execution never “returns” to the try block from a catch.
Thus, the line “This will not be printed.” is not displayed. Once the catch statement has executed, program control continues with
the next line in the program following the entire try/catch mechanism.
}
O/P:
C:\JavaApplications>javac Exc3.java
C:\JavaApplications>java Exc3
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Exc3.doMore(Exc3.java:14)
at Exc3.doStuff(Exc3.java:9)
at Exc3.main(Exc3.java:5)
O/P:
1) C:\JavaApplications>java MultiCatch
a = 0
Divide by 0: java.lang.ArithmeticException: / by zero
After try/catch blocks.
2) C:\JavaApplications>java MultiCatch 23 45
a = 2
Array index oob: java.lang.ArrayIndexOutOfBoundsException: Index 42 out of bounds for length 1
After try/catch blocks.
This program will cause a division-by-zero exception if it is started with no commandline arguments, since a will equal zero. It will
survive the division if you provide a command-line argument, setting a to something larger than zero. But it will cause an
ArrayIndexOutOfBoundsException, since the int array c has a length of 1, yet the program attempts to assign a value to c[42].
O/P:
C:\JavaApplications>java NestTry 2 3
a=2
Array index out-of-bounds: java.lang.ArrayIndexOutOfBoundsException: Index 42 out of bounds for length 1
C:\JavaApplications>java NestTry 2
a=1
Divide by 0: java.lang.ArithmeticException: / by zero
throw:
To throw an exception explicitly, use the throw statement. The general form of throw is shown here:
throw ThrowableInstance;
Here, ThrowableInstance must be an object of type Throwable or a subclass of Throwable. Primitive types, such as int or
char, as well as non-Throwable classes, such as String and Object, cannot be used as exceptions.
There are two ways you can obtain a Throwable object:
-- using a parameter in a catch clause, or
-- creating one with the new operator.
The flow of execution stops immediately after the throw statement; any subsequent statements are not executed. The nearest
enclosing try block is inspected to see if it has a catch statement that matches the type of exception. If it does find a match,
control is transferred to that statement. If not, then the next enclosing try statement is inspected, and so on. If no matching
catch is found, then the default exception handler halts the program and prints the stack trace.
Here is a sample program that creates and throws an exception. The handler that catches the exception rethrows it to the outer handler.
EX: // Demonstrate throw.
class ThrowDemo
{
static void demoproc()
{
try
{
throw new NullPointerException("demo");
}
catch(NullPointerException e)
{
System.out.println("Caught inside demoproc.");
throw e; // rethrow the exception
}
}
public static void main(String args[ ])
{
try
{
demoproc();
}
catch(NullPointerException e)
{
System.out.println("Recaught: " + e);
}
}
}
This program gets two chances to deal with the same error. First, main( ) sets up an exception context and then calls demoproc( ). The demoproc( )
method then sets up another exceptionhandling context and immediately throws a new instance of NullPointerException, which is caught on the
next line. The exception is then rethrown. Here is the resulting output:
Caught inside demoproc.
Recaught: java.lang.NullPointerException: demo
throws
If a method is capable of causing an exception that it does not handle, it must specify this behavior. so that callers of the
method can guard themselves against that exception using throws clause. A throws clause lists the types of exceptions that a
method might throw. This is the general form of a method declaration that includes a throws clause:
type method-name(parameter-list) throws exception-list
{
// body of method
}
Ex:
class ThrowsDemo1
{
static void throwOne() throws IllegalAccessException
{
System.out.println("Inside throwOne.");
throw new IllegalAccessException("demo");
}
public static void main(String args[])
{
try
{
throwOne();
}
catch (IllegalAccessException e)
{
System.out.println("Caught " + e);
}
}
}
O/P:
>java ThrowsDemo1
Inside throwOne.
Caught java.lang.IllegalAccessException: demo
finally:
finally creates a block of code that will be executed after a try/catch block has completed and before the code following the
try/catch block. The finally block will execute whether or not an exception is thrown.
If an exception is thrown, the finally block will execute even if no catch statement matches the exception. Any time a method
is about to return to the caller from inside a try/catch block, via an uncaught exception or an explicit return statement, the
finally clause is also executed just before the method returns. This can be useful for closing file handles and freeing up any
other resources that might have been allocated at the beginning of a method with the intent of disposing of them
before returning. The finally clause is optional. However, each try statement requires at least one catch or a finally clause.
EX: // Demonstrate finally.
class FinallyDemo {
// Through an exception out of the method.
static void procA()
{
try {
System.out.println("inside procA");
throw new RuntimeException("demo");
}
finally {
System.out.println("procA's finally");
}
}
// Return from within a try block.
static void procB()
{
try {
System.out.println("inside procB");
return;
}
finally {
System.out.println("procB's finally");
}
}
// Execute a try block normally.
static void procC()
{
try {
System.out.println("inside procC");
}
finally {
System.out.println("procC's finally");
}
}
public static void main(String args[]) {
try {
procA();
}
catch (Exception e) {
System.out.println("Exception caught");
}
procB();
procC();
}
}
O/P:
inside procA
procA's finally
Exception caught
inside procB
procB's finally
inside procC
procC's finally
1. Checked exceptions:
These are the exceptions that are checked at compile time. If some code within a method throws a checked exception, then the method
must either handle the exception or it must specify the exception using the throws keyword.
// Java Program to Illustrate Checked Exceptions // Where FileNotFoundException occurred
// Importing I/O classes
import java.io.*;
class CheckedEx1{
// Main driver method
public static void main(String[] args) throws IOException
{
2. Unchecked exceptions
Unchecked exceptions are runtime exceptions that are not required to be caught or declared in a throws clause. These exceptions
are usually caused by programming errors, such as attempting to access an index out of bounds in an array or attempting to
divide by zero.
Unchecked exceptions include all subclasses of the RuntimeException class, as well as the Error class and its subclasses.
Here are some examples of unchecked exceptions in Java:
1. ArrayIndexOutOfBoundsException: This exception is thrown when you attempt to access an array index
that is out of bounds.
2. NullPointerException: This exception is thrown when you attempt to access a null object reference.
3. ArithmeticException: This exception is thrown when you attempt to divide by zero or perform an invalid
arithmetic operation.
What are checked and unchecked exceptions?
Checked exceptions happen at compile time when the source code is transformed into an executable code. Unchecked exceptions
happen at runtime when the executable program starts running.
Java’s Built-in Exceptions:
C:\JavaApplications>javac CustException.java
C:\JavaApplications>java CustException
Not eligible for voting
InvalidAgeException
Although specifying a description when an exception is created is often useful, sometimes it is better to override toString( ). Here’s
why: The version of toString( ) defined by Throwable (and inherited by Exception) first displays the name of the exception
followed by a colon, which is then followed by your description. By overriding toString( ), you can prevent the exception name and
colon from being displayed.
// This program creates a custom exception type.
class MyException extends Exception
{
private int detail;
MyException(int a)
{
detail = a;
}
public String toString()
{
return "MyException[" + detail + "]";
}
}
class ExceptionDemo
{
static void compute(int a) throws MyException
{
System.out.println("Called compute(" + a + ")");
if(a > 10)
throw new MyException(a);
System.out.println("Normal exit");
}
public static void main(String args[])
{
try
{
compute(1);
compute(20);
}
catch (MyException e)
{
System.out.println("Caught " + e);
}
}
}
O/P:
Called compute(1)
Normal exit
Called compute(20)
Caught MyException[20]
The Collections Framework in Java is a comprehensive set of classes and interfaces that provide reusable
data structures to store and manipulate groups of objects. Collections in Java can store elements of different
types, including primitive types, and can dynamically grow and shrink as needed.
The List Interface:
The List interface extends Collection and declares the behavior of a collection that stores a sequence of elements. Elements
can be inserted or accessed by their position in the list, using a zero-based index. A list may contain duplicate elements.
List is a generic interface that has this declaration:
interface List<E>
Here, E specifies the type of objects that the list will hold.
⚫ It is the child interface of collection.
⚫ We can differentiate duplicates by using index.
⚫ We can preserve insertion order by using index, hence index play very important role in list interface.
⚫ To the versions of add( ) and addAll( ) defined by Collection, List adds the methods add(int, E) and addAll(int, Collection).
⚫ To obtain the object stored at a specific location, call get( ) with the index of the object.
⚫ To assign a value to an element in the list, call set( ), specifying the index of the object to be changed.
⚫ To find the index of an object, use indexOf( ) or lastIndexOf( ).
⚫ You can obtain a sublist of a list by calling subList( ), specifying the beginning and ending indexes of the sublist.
The transformation of the key into its hash code is performed automatically— you never see the hash code itself. Also,
your code can’t directly index the hash table. The advantage of hashing is that it allows the execution time of add( ),
contains( ), remove( ) and size( ) to remain constant even for large sets.
The fill ratio must be between 0.0 and 1.0, and it determines how full the hash set can be before it is resized upward.
LoadFactor/ Fill Ratio: After loading the how much factor, a new HashSet object will be created, that factor is called
as LoadFactor or FillRatio.
HashSet does not guarantee the order of its elements, because the process of hashing doesn’t usually lend itself to the
creation of sorted sets. If you need sorted storage, then another collection, such as TreeSet, is a better choice.
Here is an example that demonstrates HashSet:
Ex1: // Demonstrate HashSet.
import java.util.*;
class HashSetDemo
{
public static void main(String args[])
{
// Create a hash set.
HashSet<String> hs = new HashSet<String>();
// Add elements to the hash set.
hs.add("B");
hs.add("A");
hs.add("D");
hs.add("E");
hs.add("C");
hs.add("F");
System.out.println(hs);
System.out.println(hs.contains("C"));
System.out.println(hs.size());
hs.remove(“C”);
System.out.println(hs);
}
}
The following is the output from this program: [D, A, F, C, B, E]
true
6
[A, B, D, E, F]
System.out.println(hs);
}
}
The following is the output from this program: {34=Devi, 23=Anu,16=Divya,14=Rani}
The TreeSet Class
TreeSet extends AbstractSet and implements the NavigableSet interface. It creates a collection that uses a tree for storage.
Objects are stored in sorted, ascending order. Access and retrieval times are quite fast, which makes TreeSet an excellent
choice when storing large amounts of sorted information that must be found quickly.
TreeSet is a generic class that has this declaration:
class TreeSet<E>
Here, E specifies the type of objects that the set will hold.
TreeSet has the following constructors:
TreeSet( )
TreeSet(Collection<? extends E> c)
TreeSet(Comparator<? super E> comp)
TreeSet(SortedSet<E> ss)
⚫ The first form constructs an empty tree set that will be sorted in ascending order according to the natural order of its
elements. The second form builds a tree set that contains the elements of c.
⚫ The third form constructs an empty tree set that will be sorted according to the comparator specified by comp.
⚫ The fourth form builds a tree set that contains the elements of ss.
o Java TreeSet class contains unique elements only like HashSet.
o Java TreeSet class access and retrieval times are quite fast.
o Java TreeSet class doesn't allow null elements.
o Java TreeSet class is non-synchronized.
o Java TreeSet class maintains ascending order.
Methods:
⚫ Here is an example that demonstrates a TreeSet:
// Demonstrate TreeSet.
import java.util.*;
class TreeSetDemo
{
public static void main(String args[])
{
// Create a tree set.
TreeSet<String> ts = new TreeSet<String>();
// Add elements to the tree set.
ts.add("C");
ts.add("A");
ts.add("B");
ts.add("E");
ts.add("F");
ts.add("D");
System.out.println(ts);
}
}
The output from this program is shown here: [A, B, C, D, E, F]
TreeSet stores its elements in a tree, they are automatically arranged in sorted order, as the output confirms.
// Demonstrate TreeSet.
import java.util.*;
class TreeSetDemo
{
public static void main(String args[])
{
// Create a tree set.
TreeSet<String> ts = new TreeSet<String>();
// Add elements to the tree set.
ts.add("C");
ts.add("A");
ts.add("B");
ts.add("E");
ts.add("F");
ts.add("D");
System.out.println(ts);
TreeSet<String> ts1 = new TreeSet<String>();
ts1.add("S");
ts1.add("G");
System.out.println(ts1);
System.out.println(ts.addAll(ts1));
System.out.println("After adding sublist:"+ts);
Iterator<String> itr=ts.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
System.out.println("Descending order is:");
Iterator i=ts.descendingIterator();
while(i.hasNext())
{
System.out.println(i.next());
}
O/P:
[A, B, C, D, E, F]
[G, S]
true
After adding sublist:[A, B, C, D, E, F, G, S]
A
B
C
D
E
F
G
S
Descending order is:
S
G
F
E
D
C
B
A
Lowest Value: 12
Highest Value: 66
Initial Set: [A, B, C, D, E]
Reverse Set: [E, D, C, B, A]
Head Set: [A, B, C]
SubSet: [B, C, D, E]
TailSet: [D, E]
StringTokenizer
The processing of text often consists of parsing a formatted input string. Parsing is the division of text into a set of discrete
parts, or tokens, which in a certain sequence can convey a semantic meaning.
The StringTokenizer class provides the first step in this parsing process, often called the lexer (lexical analyzer) or scanner.
StringTokenizer implements the Enumeration interface. Therefore, given an input string, you can enumerate the individual
tokens contained in it using StringTokenizer.
To use StringTokenizer, you specify an input string and a string that contains delimiters. Delimiters are characters that
separate tokens. Each character in the delimiter string is considered a valid delimiter—for example, “,;:” sets the delimiters to
a comma, semicolon, and colon. The default set of delimiters consists of the whitespace characters: space, tab, newline, and
carriage return.
The StringTokenizer constructors are shown here:
StringTokenizer(String str)
StringTokenizer(String str, String delimiters)
StringTokenizer(String str, String delimiters, boolean delimAsToken)
In all versions, str is the string that will be tokenized. In the first version, the default delimiters are used.
In the second and third versions, delimiters is a string that specifies the delimiters.
In the third version, if delimAsToken is true, then the delimiters are also returned as tokens when the string is parsed.
Otherwise, the delimiters are not returned. Delimiters are not returned as tokens by the first two forms.
Once you have created a StringTokenizer object, the nextToken( ) method is used to extract consecutive tokens.
The hasMoreTokens( ) method returns true while there are more tokens to be extracted.
Since StringTokenizer implements Enumeration, the hasMoreElements( ) and nextElement( ) methods are also
implemented, and they act the same as hasMoreTokens( ) and nextToken( ), respectively. The StringTokenizer methods
are shown in Table 18-1.
Ex:
import java.util.StringTokenizer;
public class SimpleStToken
{
public static void main(String args[])
{
StringTokenizer st = new StringTokenizer("Welcome to Java Programming Class"," ");
while (st.hasMoreTokens())
{
//System.out.println(st.nextToken());
System.out.println(st.nextElement());
}
Date
The Date class encapsulates the current date and time. Date supports the following constructors:
Date( )
Date(long millisec)
The first constructor initializes the object with the current date and time. The second constructor accepts one argument that
equals the number of milliseconds that have elapsed since midnight, January 1, 1970. The non-deprecated methods defined by
Date are shown in Table 18-3. Date also implements the Comparable interface.
// Show date and time using only Date methods.
import java.util.Date;
class DateDemo {
public static void main(String args[])
{
// Instantiate a Date object
Date date = new Date();
// display time and date using toString()
System.out.println(date);
// Display number of milliseconds since midnight, January 1, 1970 GMT
long msec = date.getTime();
System.out.println("Milliseconds since Jan. 1, 1970 GMT = " + msec);
}
}
Sample output is shown here:
Mon Jan 01 16:28:16 CST 2007
Milliseconds since Jan. 1, 1970 GMT = 1167690496023
import java.util.Date;
There are seven types of random numbers that you can extract from a Random object. Random Boolean values are available
from nextBoolean( ). Random bytes can be obtained by calling nextBytes( ). Integers can be extracted via the nextInt( )
method. Long integers, uniformly distributed over their range, can be obtained with nextLong( ). The nextFloat( ) and
nextDouble( ) methods return a uniformly distributed float and double, respectively, between 0.0 and 1.0. Finally,
nextGaussian( ) returns a double value centered at 0.0 with a standard deviation of 1.0. This is what is known as a bell curve.
System.out.println(random.nextLong());
System.out.println(random.nextInt());
// Note: Running any of the code lines below will keep the program running as each of the methods below produce an
// unlimited random values of the corresponding type
O/P:
6
false
0.10550383396287666
0.5513571
-0.9007182115928202
[-48 0 110 -89 56 -4 89 3 88 -104 ]
7178517443475635702
672586505
Scanner:
Scanner reads formatted input and converts it into its binary form. Scanner can be used to read input from the console, a
file, a string, or any source that implements the Readable interface or ReadableByteChannel.
The Scanner Constructors:
The following sequence creates a Scanner that reads the file Test.txt:
FileReader fin = new FileReader("Test.txt");
Scanner src = new Scanner(fin);
This works because FileReader implements the Readable interface. Thus, the call to the constructor resolves to
Scanner(Readable).
1) a Scanner that reads from standard input, which is the keyboard by default:
Scanner conin = new Scanner(System.in);
This works because System.in is an object of type InputStream.
Scanner is that the same technique used to read from one source can be used to read from another.
// Use Scanner to compute an average of the values in a file.
import java.util.*;
import java.io.*;
class AvgFile
{
public static void main(String args[])throws IOException
{
int count = 0;
double sum = 0.0;
// Write output to a file.
FileWriter fout = new FileWriter("test.txt");
fout.write("2 3.4 5 6 7.4 9.1 10.5 done");
fout.close();
FileReader fin = new FileReader("test.txt");
Scanner src = new Scanner(fin);
// Read and sum numbers.
while(src.hasNext())
{
if(src.hasNextDouble())
{
sum += src.nextDouble();
count++;
}
else {
String str = src.next();
if(str.equals("done"))
break;
else {
System.out.println("File format error.");
return;
}
}
}
fin.close();
System.out.println("Average is " + sum / count);
}
}
You can use Scanner to read input that contains several different types of data—even if the order of that data is unknown in
advance. You must simply check what type of data is available before reading it. For example, consider this program:
// Use Scanner to read various types of data from a file.
import java.util.*;
import java.io.*;
class ScanMixed
{
public static void main(String args[])throws IOException
{
int i;
double d;
boolean b;
String str;
// Write output to a file.
FileWriter fout = new FileWriter("test1.txt");
fout.write("Testing Scanner 10 12.2 one true two false");
fout.close();
FileReader fin = new FileReader("test1.txt");
Scanner src = new Scanner(fin);
// Read to end.
while(src.hasNext())
{
if(src.hasNextInt())
{
i = src.nextInt();
System.out.println("int: " + i);
}
else if(src.hasNextDouble()) {
d = src.nextDouble();
System.out.println("double: " + d);
}
else if(src.hasNextBoolean()) {
b = src.nextBoolean();
System.out.println("boolean: " + b);
}
else {
str = src.next();
System.out.println("String: " + str);
}
}
fin.close();
}
}
O/P:
String: Testing
String: Scanner
int: 10
double: 12.2
String: one
boolean: true
String: two
boolean: false
MultiThreading:
A obj1=new A();
B obj2=new B();
// obj1.show();
// obj2.show();
obj1.start();
obj2.start();
}
}
Defining Runnable
class MyRunnable implements Runnable
{
public void run()
{
for(int i=0;i<10;i++)
System.out.println("Child Thread");
}
}
class ThreadDemo1
{
public static void main(String[] args)
{
MyRunnable r=new MyRunnable();
Thread t=new Thread(r);
t.start();
for(int i=0;i<10;i++)
System.out.println("Main Thread");
}
}
A obj1=new A();
B obj2=new B();
// obj1.show();
// obj2.show();
obj2.setPriority(Thread.MAX_PRIORITY);
System.out.println(obj1.getPriority());
obj1.start();
try {
Thread.sleep(2);
}catch(InterruptedException e) {
e.printStackTrace();
}
obj2.start();
}
Synchronized
Multi-Threading: Differences between multi-threading and multitasking, thread life cycle, creating threads,
thread priorities, synchronizing threads, inter thread communication.
Java’s multithreading system is built upon the Thread class, its methods, and its companion interface, Runnable.
Thread encapsulates a thread of execution. To create a new thread, your program will either extend Thread or implement the
Runnable interface.
The Thread class defines several methods that help manage threads. In a thread-based multitasking environment, the thread
is the smallest unit of dispatchable code. This means that a single program can perform two or more tasks simultaneously.
A process is, a program that is executing. Thus, process-based multitasking is the feature that allows your computer to run
two or more programs concurrently. In process-based multitasking, a program is the smallest unit of code that can be
dispatched by the scheduler.
Multithreading enables you to write very efficient programs that make maximum use of the CPU, because idle time can be
kept to a minimum. This is especially important for the interactive, networked environment in which Java operates.
Threads exist in several states. A thread can be running. It can be ready to run as soon as it gets CPU time. Arunning thread
can be suspended, which temporarily suspends its activity.A suspended thread can then be resumed, allowing it to pick up
where it left off. A thread can be blocked when waiting for a resource. At any time, a thread can be terminated, which halts its
execution immediately. Once terminated, a thread cannot be resumed.
The benefit of Java’s multithreading is that the main loop/polling mechanism is eliminated.
One thread can pause without stopping other parts of your program. For example, the idle time created when a thread reads
data from a network or waits for user input can be utilized elsewhere. Multithreading allows animation loops to sleep for a
second between each frame without causing the whole system to pause. When a thread blocks in a Java program, only the
single thread that is blocked pauses. All other threads continue to run.
- New State
- Runnable State -> start() method
- Running State -> a thead is running with run() method
- Waiting State -> sleep(), wait() method
- Dead State
In Java, the life cycle of a thread refers to the various states a thread goes through during its existence. Let’s explore these states:
1. New: When a new thread is created, it starts in the new state. At this point, the thread’s code has not yet been executed.
2. Active (Runnable/Running): After invoking the start() method, the thread transitions from the new state to the active state. Within the
active state, there are two sub-states:
a) Runnable: A thread that is ready to run but may not be actively executing at any given moment. Threads in this state wait for their turn
to run.
b) Running: When a thread gets CPU time, it moves from the runnable state to the running state. Most common transitions involve
moving from runnable to running and back.
3. Blocked or Waiting: Threads become inactive temporarily due to external factors. Examples include waiting for I/O operations or
synchronization locks. A thread in this state does not consume CPU cycles.
4. Timed Waiting: Similar to waiting, but with a specified time limit. If the time elapses, the thread becomes runnable again.
5. Terminated: A thread completes its execution or is explicitly terminated. Once terminated, a thread cannot be restarted.
Synchronization in Java
Synchronization in Java is the capability to control the access of multiple threads to any shared resource.
Java Synchronization is better option where we want to allow only one thread to access the shared resource.
Why use Synchronization?
The synchronization is mainly used to a) To prevent thread interference. B) To prevent consistency problem.
Producer-Consumer problem
The Producer-Consumer problem is a classical multi-process synchronization problem, that is we are trying to achieve synchronization
between more than one process.
There is one Producer in the producer-consumer problem, Producer is producing some items, whereas there is one Consumer that is
consuming the items produced by the Producer. The same memory buffer is shared by both producers and consumers which is of fixed-
size.
The task of the Producer is to produce the item, put it into the memory buffer, and again start producing items. Whereas the task of the
Consumer is to consume the item from the memory buffer.
o The producer should produce data only when the buffer is not full. In case it is found that the buffer is full, the producer is not
allowed to store any data into the memory buffer.
o Data can only be consumed by the consumer if and only if the memory buffer is not empty. In case it is found that the buffer
is empty, the consumer is not allowed to use any data from the memory buffer.
o Accessing memory buffer should not be allowed to producer and consumer at the same time.
class Buffer{
int a;
boolean produced = false;
//starting threads.
p.start();
c.start();
}
}