0% found this document useful (0 votes)
4 views51 pages

Unit2 Part2

The document covers input/output basics in Java, detailing Byte Streams and Character Streams, including their classes and methods for reading and writing files. It explains multithreading concepts such as thread life cycle and synchronization. Additionally, it provides Java code examples for reading from and writing to files using both byte and character streams.

Uploaded by

aarya2312022
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views51 pages

Unit2 Part2

The document covers input/output basics in Java, detailing Byte Streams and Character Streams, including their classes and methods for reading and writing files. It explains multithreading concepts such as thread life cycle and synchronization. Additionally, it provides Java code examples for reading from and writing to files using both byte and character streams.

Uploaded by

aarya2312022
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

Unit-2 part2

Input /Output Basics: Byte Streams and Character Streams, Reading and
Writing File in Java.
Multithreading: Thread, Thread Life Cycle, Creating Threads, Thread
Priorities, Synchronizing Threads, Inter-thread Communication.
ByteStream
• ByteStream classes are used to read bytes from the input
stream and write bytes to the output stream. In other words,
we can say that ByteStream classes read/write the data of
8-bits. We can store video, audio, characters, etc., by using
ByteStream classes. These classes are part of the java.io
package.
• The ByteStream classes are divided into two types of
classes, i.e., InputStream and OutputStream. These classes
are abstract and the super classes of all the Input/Output
stream classes.
InputStream Class
• The InputStream class provides methods to read bytes from
a file, console or memory.
• It is an abstract class and can't be instantiated; however,
various classes inherit the InputStream class and override
its methods.
SN Class Description
1 BufferedInputStream This class provides methods to read bytes from the buffer.

2 ByteArrayInputStream This class provides methods to read bytes from the byte array.

3 DataInputStream This class provides methods to read Java primitive data types.

4 FileInputStream This class provides methods to read bytes from a file.

5 FilterInputStream This class contains methods to read bytes from the other input
streams, which are used as the primary source of data.

6 ObjectInputStream This class provides methods to read objects.

7 PipedInputStream This class provides methods to read from a piped output stream to
which the piped input stream must be connected.

8 SequenceInputStream This class provides methods to connect multiple Input Stream and
read data from them.
The InputStream class contains various methods to read the data
from an input stream. These methods are overridden by the classes
that inherit the InputStream class.
SN Method Description
1 int read() This method returns an integer, an integral representation of the next available
byte of the input. The integer -1 is returned once the end of the input is
encountered.
2 int read (byte buffer []) This method is used to read the specified buffer length bytes from the input and
returns the total number of bytes successfully read. It returns -1 once the end of
the input is encountered.
3 int read (byte buffer [], int This method is used to read the 'nBytes' bytes from the buffer starting at a
loc, int nBytes) specified location, 'loc'. It returns the total number of bytes successfully read
from the input. It returns -1 once the end of the input is encountered.
4 int available () This method returns the number of bytes that are available to read.
5 Void mark(int nBytes) This method is used to mark the current position in the input stream until the
specified nBytes are read.
6 void reset () This method is used to reset the input pointer to the previously set mark.
7 long skip (long nBytes) This method is used to skip the nBytes of the input stream and returns the total
number of bytes that are skipped.
8 void close () This method is used to close the input source. If an attempt is made to read even
OutputStream Class
• The OutputStream is an abstract class that is used to write
8-bit bytes to the stream.
• It is the superclass of all the output stream classes.
• This class can't be instantiated; however, it is inherited by
various subclasses
SN Class Description
1 BufferedOutputStream This class provides methods to write the bytes to the buffer.

2 ByteArrayOutputStream This class provides methods to write bytes to the byte array.

3 DataOutputStream This class provides methods to write the java primitive data types.

4 FileOutputStream This class provides methods to write bytes to a file.

5 FilterOutputStream This class provides methods to write to other output streams.

6 ObjectOutputStream This class provides methods to write objects.

7 PipedOutputStream It provides methods to write bytes to a piped output stream.

8 PrintStream It provides methods to print Java primitive data types.


The OutputStream class provides various methods to write bytes to
the output streams. The methods are given in the following table.
SN Method Description
1 void write (int i) This method is used to write the specified single byte to the
output stream.
2 void write (byte buffer [] ) It is used to write a byte array to the output stream.

3 Void write(bytes buffer[],int loc, int It is used to write nByte bytes to the output stream from the
nBytes) buffer starting at the specified location.

4 void flush () It is used to flush the output stream and writes the pending
buffered bytes.
5 void close () It is used to close the output stream. However, if we try to close
the already closed output stream, the IOException will be
thrown by this method.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOStreams {
public static void main(String args[]) throws IOException {
//Creating FileInputStream object
File file = new File("D:/myFile.txt");
FileInputStream fis = new FileInputStream(file);
byte bytes[] = new byte[(int) file.length()];
//Reading data from the file
fis.read(bytes);
//Writing data to another file
File out = new File("D:/CopyOfmyFile.txt");
FileOutputStream outputStream = new FileOutputStream(out);
//Writing data to the file
outputStream.write(bytes);
outputStream.flush();
System.out.println("Data successfully written in the specified file");
}
• Imports: Necessary classes are imported for file I/O operations (File, FileInputStream, FileOutputStream, and IOException).
• Class Declaration: The class IOStreams is declared, which will contain the main method.
• main Method: The main method is declared with IOException in the method signature, indicating that it may throw an
IOException which must be handled by the method that calls main or the JVM.
• File Input:
• A File object is created to represent a file named "myFile.txt" located in the D drive.
• A FileInputStream is opened for that file, which is used for reading the file's contents.
• Reading Data:
• A byte array bytes is initialized to the length of the file. This will be used to store the file's data.
• The read method of FileInputStream is used to read the data from the file and store it in the bytes array.
• File Output:
• Another File object is created for the output file named "CopyOfmyFile.txt" also in the D drive.
• A FileOutputStream is created for the output file.
• Writing Data:
• The write method of FileOutputStream writes the content of bytes to the output file.
• flush is called on the outputStream to ensure all data is written out to the file.
• Completion Message: The program prints a message to the console indicating that the data has been successfully written
to the specified file.
CharacterStream Classes in Java
• The java.io package provides CharacterStream classes to
overcome the limitations of ByteStream classes, which can only
handle the 8-bit bytes and is not compatible to work directly with
the Unicode characters.
• CharacterStream classes are used to work with 16-bit Unicode
characters.
• They can perform operations on characters, char arrays and
Strings.
• However, the CharacterStream classes are mainly used to read
characters from the source and write them to the destination.
• For this purpose, the CharacterStream classes are divided into
two types of classes, i.e., Reader class and Writer class.
Reader Class
• Reader class is used to read the 16-bit characters from the
input stream. However, it is an abstract class and can't be
instantiated, but there are various subclasses that inherit
the Reader class and override the methods of the Reader
class. All methods of the Reader class throw an
IOException.
SN Class Description
1. BufferedReader This class provides methods to read characters from the buffer.
2. CharArrayReader This class provides methods to read characters from the char array.
3. FileReader This class provides methods to read characters from the file.
4. FilterReader This class provides methods to read characters from the underlying character input stream.

5 InputStreamReader This class provides methods to convert bytes to characters.


6 PipedReader This class provides methods to read characters from the connected piped output stream.

7 StringReader This class provides methods to read characters from a string.


Reader class methods
SN Method Description
1 int read() This method returns the integral representation of the next character present in the input. It returns -1 if the end of the
input is encountered.

2 int read(char buffer[]) This method is used to read from the specified buffer. It returns the total number of characters successfully read. It returns
-1 if the end of the input is encountered.

3 int read(char buffer[], int loc, int This method is used to read the specified nChars from the buffer at the specified location. It returns the total number of
nChars) characters successfully read.

4 void mark(int nchars) This method is used to mark the current position in the input stream until nChars characters are read.

5 void reset() This method is used to reset the input pointer to the previous set mark.

6 long skip(long nChars) This method is used to skip the specified nChars characters from the input stream and returns the number of characters
skipped.

7 boolean ready() This method returns a boolean value true if the next request of input is ready. Otherwise, it returns false.

8 void close() This method is used to close the input stream. However, if the program attempts to access the input, it generates
IOException.
Writer Class
• Writer class is used to write 16-bit Unicode characters to
the output stream. The methods of the Writer class
generate IOException. Like Reader class, Writer class is also
an abstract class that cannot be instantiated; therefore, the
subclasses of the Writer class are used to write the
characters
SN Class onto the output stream.
Description
1 BufferedWriter This class provides methods to write characters to the buffer.

2 FileWriter This class provides methods to write characters to the file.


3 CharArrayWriter This class provides methods to write the characters to the character array.

4 OutpuStreamWriter This class provides methods to convert from bytes to characters.

5 PipedWriter This class provides methods to write the characters to the piped output stream.

6 StringWriter This class provides methods to write the characters to the string.
To write the characters to the output
stream, the Write class provides
various
SN Method
methods
Description
1 void write() This method is used to write the data to the output stream.

2 void write(int i) This method is used to write a single character to the output stream.

3 Void write(char buffer[]) This method is used to write the array of characters to the output stream.

4 void write(char buffer This method is used to write the nChars characters to the character array
[],int loc, int nChars) from the specified location.

5 void close () This method is used to close the output stream. However, this generates the
IOException if an attempt is made to write to the output stream after closing
the stream.
6 void flush () This method is used to flush the output stream and writes the waiting
buffered characters.
Java Code to Read from a File using
Character Streams
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
public class ReadTextFile {
public static void main(String[] args) {
String filePath = "D:/example.txt"; // Path to the text file
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // Prints each line in the file
}
} catch (IOException e) {
System.out.println("Error reading the file: " + e.getMessage());
}
}
}
Explanation
• Imports: The FileReader and BufferedReader classes are imported from
java.io. These classes are used for reading text files.
• Class Declaration: ReadTextFile class contains the main method.
• BufferedReader: This class wraps a FileReader, buffering the inputs from the
file, which enhances efficiency by reducing the number of disk accesses.
• FileReader: This is used to read characters from the file. It reads the file's
content directly.
• Reading the File: Inside the try block, the file is read line by line using
readLine() method until null is returned (end of file).
• Exception Handling: The try-with-resources statement ensures that the
BufferedReader is closed after the file is read, even if an error occurs.
Java Code to Write to a File using
Character Streams
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;
public class WriteTextFile {
public static void main(String[] args) {
String filePath = "D:/output.txt"; // Path to the text file
String content = "Hello, this is an example of writing to a file.\n";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
writer.write(content); // Writes the string content to the file
writer.newLine(); // Adds a new line
writer.write("This is another line."); // Writes another line to the file
} catch (IOException e) {
System.out.println("Error writing to the file: " + e.getMessage());
}
}
}
Explanation
• Imports: The FileWriter and BufferedWriter classes are imported from java.io.
• Class Declaration: WriteTextFile class contains the main method.
• BufferedWriter: This class wraps a FileWriter, buffering the outputs to the file. This
improves performance by minimizing the number of disk write operations
required.
• FileWriter: It’s used to write characters to the file. It can overwrite the file content
or append to it depending on the constructor used (new FileWriter(filePath, true)
would append).
• Writing the File: The file is opened and written to using write() method. newLine()
adds a new line character to the file.
• Exception Handling: The try-with-resources statement ensures that the
BufferedWriter is closed properly after the writing is completed, even if an error
occurs.
Java Files
• File handling is an important part of any application.
• Java has several methods for creating, reading,
updating, and deleting files.
• Java File Handling
• The File class from the java.io package, allows us to work with files.
• To use the File class, create an object of the class, and specify the
filename or directory name:
• import java.io.File; // Import the File class
• File myObj = new File("filename.txt"); // Specify the filename
The File class has many useful methods for creating and getting
information about files. For example:
Method Type Description
canRead() Boolean Tests whether the file is readable or not
canWrite() Boolean Tests whether the file is writable or not
createNewFile() Boolean Creates an empty file
delete() Boolean Deletes a file
exists() Boolean Tests whether the file exists
getName() String Returns the name of the file
getAbsolutePath() String Returns the absolute pathname of the file
length() Long Returns the size of the file in bytes
list() String[] Returns an array of the files in the directory
mkdir() Boolean Creates a directory
Java Create and Write To Files
Create a File
• To create a file in Java, you can use the createNewFile() method.
• This method returns a boolean value: true if the file was successfully
created, and false if the file already exists.
• Note that the method is enclosed in a try...catch block.
• This is necessary because it throws an IOException if an error occurs
(if the file cannot be created for some reason):
import java.io.File; // Import the File class
import java.io.IOException; // Import the IOException class to handle errors
public class CreateFile {
public static void main(String[] args) {
try {
File myObj = new File("filename.txt");
if (myObj.createNewFile()) {
System.out.println("File created: " + myObj.getName());
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
}
Write To a File
import java.io.FileWriter; // Import the FileWriter class
import java.io.IOException; // Import the IOException class to handle errors
public class WriteToFile {
public static void main(String[] args) {
try {
FileWriter myWriter = new FileWriter("filename.txt");
myWriter.write("Files in Java might be tricky, but it is fun enough!");
myWriter.close();
System.out.println("Successfully wrote to the file.");
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
}
Read a File
import java.io.File; // Import the File class
import java.io.FileNotFoundException; // Import this class to handle errors
import java.util.Scanner; // Import the Scanner class to read text files
public class ReadFile {
public static void main(String[] args) {
try {
File myObj = new File("filename.txt");
Scanner myReader = new Scanner(myObj);
while (myReader.hasNextLine()) {
String data = myReader.nextLine();
System.out.println(data);
}
myReader.close();
} catch (FileNotFoundException e) {
System.out.println("An error occurred.");
e.printStackTrace();
} } }
Delete a File
import java.io.File; // Import the File class
public class DeleteFile {
public static void main(String[] args) {
File myObj = new File("filename.txt");
if (myObj.delete()) {
System.out.println("Deleted the file: " + myObj.getName());
} else {
System.out.println("Failed to delete the file.");
}
}
}
Delete a Folder
import java.io.File;
public class DeleteFolder {
public static void main(String[] args) {
File myObj = new File("C:\\Users\\MyName\\Test");
if (myObj.delete()) {
System.out.println("Deleted the folder: " + myObj.getName());
} else {
System.out.println("Failed to delete the folder.");
}
}
}
Multi-thread
• Multi-threading means multiple flow of control.
• Multi-threading programming is a conceptual paradigm for
programming where one can divide a program into two or more
processes which can be run in parallel.
• There are two main advantages of multi-threading :
• First, program with multiple threads will, in general, result in better utilization
of system resources, including the CPU, because another line of execution
can grab the CPU when one line of execution is blocked.
• Second, there are several problems better solved by multiple threads. For
example, we can easily write a multi-threaded program to show animation,
play music, display documents, and download files from the network at the
same time.
• Java is a multi-threaded language. Java allows to write a
program where more than one processes can be executed
concurrently within the single program.
• Java's threads are often referred to as light weight threads,
which means that they run in the same memory space.
• Because Java threads run in the same memory space, they can
easily communicate among themselves because an object in
one thread can call a method in another thread without any
overhead from the operating system.
• Threads
• A thread is a lightweight sub-process that runs concurrently with other
threads.
• Threads can be used to perform multiple tasks simultaneously, which
can improve the performance and responsiveness of an application.
Life cycle of threads
Each thread is always in one of five
states
• Newborn : When a thread is created (by new statement ) but not yet to
run, it is called in Newborn state. In this state, the local data members are
allocated and initialized.
• Runnable : The Runnable state means that a thread is ready to run and is
awaiting for the control of the processor, or in other words, threads are in
this state in a queue and wait their turns to be executed.
• Running : Running means that the thread has control of the processor, its
code is currently being executed and thread will continue in this state until
it get preempted by a higher priority thread, or until it relinquishes control.
• Blocked : A thread is Blocked means that it is being prevented from the
Runnable ( or Running) state and is waiting for some event in order for it to
reenter the scheduling queue.
• Dead : A thread is Dead when it finishes its execution or is stopped (killed)
by another thread.
• Threads move from one state to another via a variety of means. Methods to
control them are as follows:
• start ( ) : A newborn thread with this method enter into Runnable state and Java
run time create a system thread context and starts it running. This method for a
thread object can be called once only
• stop( ) : This method causes a thread to stop immediately. This is often an abrupt
way to end a thread.
• suspend( ) : This method is different from stop( ) method. It takes the thread and
causes it to stop running and later on can be restored by calling it again.
• resume ( ) : This method is used to revive a suspended thread. There is no
gurantee that the thread will start running right way, since there might be a higher
priority thread running already, but, resume()causes the thread to become eligible
for running.
• sleep (int n ) : This method causes the run time to put the current thread to sleep
for n milliseconds. After n milliseconds have expired, this thread will become
elligible to run again.
• yield( ) : The yield() method causes the run time to switch the context from the
current thread to the next available runnable thread. This is one way to ensure that
the threads at lower priority do not get started.
Methods for Creating Threads
There are two primary ways to create threads in Java:
• Extending the Thread class:
• Define a class that extends the Thread class.
• Override the run() method to contain the code you want the thread to
execute.
• Create an instance of your class and call its start() method.

• Implementing the Runnable interface:


• Define a class that implements the Runnable interface.
• Implement the run() method to contain the code you want the thread to
execute.
• Create an instance of your Runnable class and pass it to the constructor of a
Thread object.
• Call the start() method on the Thread object.
Example: Extending the Thread Class

class MyThread extends Thread {


@Override
public void run() {
System.out.println("MyThread is running");
}
} MyThread is running
public class Main {
MyThread is running
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
Example: Implementing the Runnable
Interface
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable is running");
}
} MyRunnable is running
public class Main30 {
MyRunnable is running
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start();
thread2.start();
}
}
class ThreadLifecycleDemo implements Runnable { // Step 2: Waiting for a block to be synchronized
public void run() { synchronized(this) {

Thread th = Thread.currentThread(); try {


// This will put the thread into a WAITING state
System.out.println("Thread state is: " +
th.getState()); // Output the current state this.wait(1000);
// Step 1: Running } catch (InterruptedException e) {
try { e.printStackTrace();

// This will put the thread into a } }


TIMED_WAITING state // Step 3: Running again
Thread.sleep(1500); try {
System.out.println("Thread state after sleep(): // Another sleep to simulate work
" + th.getState()); // TIMED_WAITING Thread.sleep(500);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
System.out.println("Thread state at the end of run
method(): " + th.getState()); // TERMINATED soon after run()
completes
}
public static void main(String[] args) throws synchronized(obj) {
InterruptedException {
// Notify to demonstrate WAITING to RUNNING
ThreadLifecycleDemo obj = new
ThreadLifecycleDemo(); obj.notify();
Thread t1 = new Thread(obj); }
// New thread // Wait for the thread to finish
System.out.println("Thread state after creation: " + t1.join();
t1.getState()); // NEW System.out.println("Thread state after join(): " +
t1.start(); // Thread started t1.getState()); // TERMINATED
// Just after starting, it should be in RUNNABLE or }
possibly RUNNING }
System.out.println("Thread state after calling Thread state after creation: NEW
start(): " + t1.getState()); // RUNNABLE
Thread state after calling start(): RUNNABLE
// Let's wait a bit to see it in TIMED_WAITING state
Thread state is: RUNNABLE
Thread.sleep(200); // Main thread sleeps
Thread state in sleep(): TIMED_WAITING
System.out.println("Thread state in sleep(): " +
t1.getState()); // TIMED_WAITING Thread state after sleep(): RUNNABLE
Thread state at the end of run method(): RUNNABLE
Thread state after join(): TERMINATED
• Class Definition
class ThreadLifecycleDemo implements Runnable {
• The ThreadLifecycleDemo class implements the Runnable interface,
which requires defining the run() method where the thread's
execution behavior is specified.
• run() Method
• This is where the behavior of the thread is defined:
public void run() {
Thread th = Thread.currentThread();
System.out.println("Thread state is: " + th.getState());
• Thread.currentThread() retrieves a reference to the currently
executing thread.
• The state of the thread is printed, which should initially be
RUNNABLE as soon as it's running.
Synchronizing Threads
• Synchronization is the process of ensuring that multiple threads can
access shared resources safely. Without synchronization, it is
possible for two threads to access the same resource at the same
time, which can lead to data corruption.
• There are two main ways to synchronize threads in Java:
• Using locks:
• A lock is an object that allows only one thread to access a shared resource at
a time. To synchronize access to a shared resource, you can use a lock to
acquire a lock before accessing the resource and release the lock after
accessing the resource.
• Using synchronized methods:
• A synchronized method is a method that can only be executed by one thread
at a time. To synchronize access to a shared resource, you can declare a
method as synchronized.
• Sleep to simulate work and induce TIMED_WAITING
Thread.sleep(1500);
• The thread is put to sleep for 1500 milliseconds. During this time, the
thread is in the TIMED_WAITING state because it's waiting for the sleep
duration to finish.
• Synchronized Block and WAITING State
synchronized(this) {
this.wait(1000);
}
• The synchronized keyword is used to lock on the current instance (this) of
ThreadLifecycleDemo. This ensures that no other thread can enter a block
synchronized on the same object.
• this.wait(1000) causes the thread to wait up to 1000 milliseconds, or until
another thread calls notify() or notifyAll() on the same object. During
wait(), the thread releases the lock on the object and enters the WAITING
state.
• Main Method
• This is where the thread is created, started, and its states are monitored:
ThreadLifecycleDemo obj = new ThreadLifecycleDemo();
Thread t1 = new Thread(obj);
• An instance of ThreadLifecycleDemo is created.
• A new Thread object (t1) is created with obj as its target (the Runnable object).
• Print Initial State (NEW)
System.out.println("Thread state after creation: " + t1.getState());
• Prints the state of t1 just after creation, which should be NEW.
• Starting the Thread (RUNNABLE and possibly RUNNING)
t1.start();
System.out.println("Thread state after calling start(): " + t1.getState());
• t1.start() transitions the thread from NEW to RUNNABLE. It may immediately
become RUNNING depending on the scheduler.
• The state of the thread just after starting is printed.
• Timed Waiting
Thread.sleep(200);
System.out.println("Thread state in sleep(): " + t1.getState());
• The main thread sleeps for 200 milliseconds to allow t1 to enter and show TIMED_WAITING during
its own sleep.
• Ending WAITING and Final State (TERMINATED)
synchronized(obj) {
obj.notify();
}
t1.join();
System.out.println("Thread state after join(): " + t1.getState());
• The main thread synchronizes on obj and calls notify() to wake t1 if it's in the waiting state.
• t1.join() waits for t1 to finish executing.
• The final state of t1 after it has completed execution (terminated) is printed.
• Conclusion
• This program illustrates transitions between different thread states using synchronization methods
(synchronized, wait, notify) and thread control methods (sleep, join). It's a concise demonstration of thread
behavior in Java, highlighting the effect of different methods on the thread's lifecycle.
Choosing an Approach
• At this point, you might be wondering why Java has two ways to create child
threads, and which approach is better.
• The answers to these questions turn on the same point.
• The Thread class defines several methods that can be overridden by a derived
class. Of these methods, the only one that must be overridden is run( ).
• This is, of course, the same method required when you implement Runnable.
• Many Java programmers feel that classes should be extended only when they are
being enhanced or modified in some way.
• So, if you will not be overriding any of Thread’s other methods, it is probably best
simply to implement Runnable.
• Also, by implementing Runnable, your thread class does not need to inherit
Thread, making it free to inherit a different class. Ultimately, which approach to
use is up to you.
Inter-thread Communication
• To avoid polling, Java includes an elegant interprocess communication
mechanism via the wait( ), notify( ), and notifyAll( ) methods.
• These methods are implemented as final methods in Object, so all classes
have them.
• All three methods can be called only from within a synchronized context.
Although conceptually advanced from a computer science perspective, the
rules for using these methods are actually quite simple:
• wait( ) tells the calling thread to give up the monitor and go to sleep until some
other thread enters the same monitor and calls notify( ) or notifyAll( ).
• notify( ) wakes up a thread that called wait( ) on the same object.
• notifyAll( ) wakes up all the threads that called wait( ) on the same object. One of
the threads will be granted access.
• These methods are declared within Object, as shown here: final void wait( )
throws InterruptedException final void notify( ) final void notify All( )
• class Store { • public synchronized void consume() {
• private int product = 0; • while (product < 1) {
• try {
• public synchronized void produce() { • wait(); // Wait until a product is
produced
• while (product >= 1) {
• } catch (InterruptedException e) {
• try {
• e.printStackTrace();
• wait(); // Wait until the product is
consumed • }
• } catch (InterruptedException e) { • }
• e.printStackTrace(); • product--; // Consume the product
• } • System.out.println("Consumed one
product");
• }
• notifyAll(); // Notify the producer that the
• product++; // Produce a product product has been consumed
• System.out.println("Produced one • }
product");
• }
• notifyAll(); // Notify the consumer that a
product is available
• }
• class Producer implements • class Consumer implements
Runnable { Runnable {
• private Store store; • private Store store;

• public Producer(Store store) { • public Consumer(Store store) {


• this.store = store; • this.store = store;
• } • }

• public void run() { • public void run() {


• for (int i = 0; i < 5; i++) { • for (int i = 0; i < 5; i++) {
• store.produce(); • store.consume();
• } • }
• } • }
•} •}
• public class Sync { • Produced one product
• public static void main(String[] • Consumed one product
args) { • Produced one product
• Store store = new Store(); • Consumed one product
• Thread producerThread = new • Produced one product
Thread(new Producer(store));
• Consumed one product
• Thread consumerThread = new
Thread(new Consumer(store)); • Produced one product
• Consumed one product
• producerThread.start(); • Produced one product
• consumerThread.start(); • Consumed one product
• }
•}
Thread Priorities
• Each thread in Java has a priority, which determines the order in
which threads are scheduled for execution. The higher the priority of
a thread, the more likely it is to be scheduled for execution.
• There are ten thread priorities in Java, ranging from MIN_PRIORITY
(1) to MAX_PRIORITY (10). By default, all threads are created with a
priority of NORM_PRIORITY (5).
• You can change the priority of a thread by calling the setPriority()
method. However, it is important to note that the thread scheduler is
not guaranteed to schedule threads in order of priority.
1. public static int MIN_PRIORITY
2. public static int NORM_PRIORITY
3. public static int MAX_PRIORITY
• class PriorityDemo implements Runnable { • // Setting priorities
• public void run() { • t1.setPriority(Thread.MIN_PRIORITY); // Priority 1
• System.out.println(Thread.currentThread().getName() + • t2.setPriority(Thread.NORM_PRIORITY); // Priority 5
" starting with priority " +
Thread.currentThread().getPriority()); • t3.setPriority(Thread.MAX_PRIORITY); // Priority 10
• long count = 0; • // Starting threads
• for (int i = 0; i < 10000000; i++) { • t1.start();
• count += i; • t2.start();
• } • t3.start();
• System.out.println(Thread.currentThread().getName() + • try {
" ending."); • // Wait for threads to finish
• } • t1.join();
• t2.join();
• public static void main(String[] args) { • t3.join();
• // Creating threads • } catch (InterruptedException e) {
• Thread t1 = new Thread(new PriorityDemo(), "Thread • System.out.println("Main thread interrupted.");
1");
• Thread t2 = new Thread(new PriorityDemo(), "Thread • }
2"); • System.out.println("Main thread ending.");
• Thread t3 = new Thread(new PriorityDemo(), "Thread • }
3");
• }
• Thread 3 starting with priority 10
• Thread 2 starting with priority 5
• Thread 3 ending.
• Thread 2 ending.
• Thread 1 starting with priority 1
• Thread 1 ending.
• Main thread ending.
Inter-thread Communication
• Inter-thread communication is the process of threads
communicating with each other. There are two main ways for
threads to communicate with each other:
• Using shared variables:
• Threads can communicate with each other by sharing
variables. However, it is important to synchronize access to shared
variables to avoid data corruption.
• Using message passing:
• Message passing is a technique in which threads communicate with
each other by sending and receiving messages. Message passing is a
more reliable way for threads to communicate with each other than
sharing variables.

You might also like