BSC306A OOC Module 5 Class Notes_programs
BSC306A OOC Module 5 Class Notes_programs
I)Multithreaded Programming ,
I) Multithreaded Programming ,
Topics: The Java Thread Model, The Main Thread, Creating a Thread, Creating Multiple
Threads, Using isAlive() and join(), Thread Priorities, Synchronization, Interthread
Communication, Suspending, Resuming, and Stopping Threads, Obtaining a Thread’s State.
Mutli-threading: more than one threads being executed by CPU simulateneously(one after
Multithreading is a Java feature that allows concurrent execution of two or more parts of a
program for maximum utilization of CPU. Each part of such program is called a thread. So,
threads are light-weight processes within a process.
void task2()
{
System.out.println("This is Task2");
}
void task3()
{
System.out.println("This is Task3");
}
}
class Thread2a
{
public static void main(String args[])
{
//creating the instance of the MyThread class
MyThread ob1=new MyThread();
Example:
class MyThread implements Runnable
{
public void run()
{
task1();
task2();
task3();
}
void task1()
{
System.out.println("This is Task1");
}
void task2()
{
System.out.println("This is Task2");
}
void task3()
{
System.out.println("This is Task3");
}
}
class Thread2
{
public static void main(String args[])
{
//crteating a object of MyThread
MyThread ob1=new MyThread();
Example 1:
//run method without call to sleep method
Output
Output:
Thread 14: Count 1
Thread 15: Count 1
Thread 14: Count 2
Thread 15: Count 2
Thread 14: Count 3
Thread 15: Count 3
Thread 15: Count 4
Thread 14: Count 4
Thread 15: Count 5
Thread 14: Count 5
Using isAlive() and join():
Sometimes one thread needs to know when other thread is terminating. In
java, isAlive() and join() are two different methods that are used to check whether a thread has
finished its execution or not.
The isAlive() method returns true if the thread upon which it is called is still running .
Using join() method, we tell our thread to wait until the specified thread completes its execution.
In this example, we are using join() method to ensure that thread finished its execution before
starting other thread. It is helpful when we want to executes multiple threads based on our
requirement.
public class MyThread extends Thread
{
public void run()
{
System.out.println("r1 ");
try {
Thread.sleep(500);
}catch(InterruptedException ie){ }
System.out.println("r2 ");
}
public static void main(String[] args)
{
MyThread t1=new MyThread();
MyThread t2=new MyThread();
t1.start();
try{
t1.join(); //Waiting for t1 to finish
}catch(InterruptedException ie){}
t2.start();
}
}
Output:
r1
r2
r1
r2
In this above program join() method on thread t1 ensures that t1 finishes it process before thread
t2 starts..
Priority of a Thread (Thread Priority)
Each thread has a priority. Priorities are represented by a number between 1 and 10. In most
cases, the thread scheduler schedules the threads according to their priority (known as
preemptive scheduling). But it is not guaranteed because it depends on JVM specification that
which scheduling it chooses. Note that not only JVM a Java programmer can also assign the
priorities of a thread explicitly in a Java program.
Let's discuss the setter and getter method of the thread priority.
public final int getPriority(): The java.lang.Thread.getPriority() method returns the priority of
the given thread.
//getting current thread information and changing the name of the thread and thread priority
Example:
class ThreadExample
Thread t=Thread.currentThread();
t.setName("Bengaluru");
t.setPriority(10);
o/p:
Synchronization:
Synchronization in Java is a mechanism to control the access of multiple threads to shared resources. It is
essential in a multithreaded environment to prevent thread interference and consistency problems.
Synchronization ensures that only one thread can access the shared resource at a time, thus preserving the
integrity of the data.
Why Synchronization is Needed?
In a multithreaded environment, multiple threads may try to modify the same data simultaneously, leading
to data inconsistency. Consider the following example without synchronization:
class Counter {
private int count = 0;
ThreadDemo(Counter counter) {
this.counter = counter;
}
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
In the above code, the `Counter` object is shared between two threads (`t1` and `t2`). Both threads
increment the counter 1000 times. Ideally, the final count should be 2000. However, due to the
lack of synchronization, the final count may be less than 2000 because the increment operation
(`count++`) is not atomic and can be interrupted.
How to Use Synchronization in Java?
1. Synchronized Method:
Synchronize the entire method to ensure only one thread can execute it at a time.
class Counter {
private int count = 0;
2. Synchronized Block:
Synchronize a block of code instead of the entire method, providing more control and efficiency.
class Counter {
private int count = 0;
class Counter {
private static int count = 0;
4. Locks:
Use `java.util.concurrent.locks.Lock` for more sophisticated thread synchronization.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
class Counter {
private int count = 0;
ThreadDemo(Counter counter) {
this.counter = counter;
}
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Output:
Final count: 2000
In this example, the `increment()` method is synchronized. This ensures that only one thread can
execute this method at a time, preserving the integrity of the `count` variable.
Interthread Communication:
Inter-thread communication in Java is a technique through which multiple threads
communicate with each other. It provides an efficient way through which more than one thread
communicate with each other by reducing CPU idle time. CPU idle time is a process in which
CPU cycles are not wasted.
When more than one threads are executing simultaneously, sometimes they need to communicate
with each other by exchanging information with each other. A thread exchanges information
before or after it changes its state.
There are several situations where communication between threads is important. For example,
suppose that there are two threads A and B. Thread B uses data produced by Thread A and
performs its task.
If Thread B waits for Thread A to produce data, it will waste many CPU cycles. But if threads A
and B communicate with each other when they have completed their tasks, they do not have to
wait and check each other’s status every time.
Thus, CPU cycles will not waste. This type of information exchanging between threads is called
inter thread communication in Java.
The notify() method wakes up a single thread that called wait() method on the same object. If
more than one thread is waiting, this method will awake one of them.
notifyAll() Method in Java
The notifyAll() method is used to wake up all threads that called wait() method on the same
object. The thread having the highest priority will run first.
In the producer and consumer problem, we will use the same technique. It consists of four
classes:
• Q where we will try to synchronize data.
• Producer where Producer thread will produce data (or some goods).
• Consumer where Consumer thread will wait for Producer to produce data. When the Producer
thread completes the production of data, the Consumer thread will take that data and use it.
Example :
// Implementation of Producer and Consumer.
public class Q
{
int i;
boolean valueSet = false;
synchronized void produce(int i)
{
if(valueSet)
try
{
wait();
}
catch(InterruptedException ie)
{
System.out.println(ie);
}
this.i = i;
valueSet = true;
System.out.println("Data Produced: " +i);
notify();
}
synchronized int consume()
{
if(!valueSet)
try {
wait();
}
catch(InterruptedException ie){
System.out.println(ie);
}
System.out.println("Data Consumed: " + i);
valueSet = false;
notify();
return I;
}
}
public class Producer extends Thread
{
Q q;
Producer(Q q)
{
this.q = q;
}
public void run()
{
for(int j = 1; j <= 5; j++) {
q.produce(j);
}
}
}
public class Consumer extends Thread
{
Q q;
Consumer(Q q)
{
this.q = q;
}
public void run()
{
for(int k = 0; k <= 5; k++) {
q.consume();
}
}
}
public class ThreadCommunication {
public static void main(String[] args)
{
Q q = new Q();
output:
Data Produced: 1
Data Consumed: 1
Data Produced: 2
Data Consumed: 2
Data Produced: 3
Data Consumed: 3
Data Produced: 4
Data Consumed: 4
Data Produced: 5
Data Consumed: 5
In Java, a thread can be suspended by using the wait() method on an object. This method
suspends thread execution until it is notified by another thread using the notify() method.
An interrupted thread's execution can be picked back up by notifying the waiting thread using the
notify() method.
To halt a running thread, use a boolean flag to signal the thread to stop gracefully.
Consider the following program as an example demonstrating the suspending, resuming, and
stopping of threads in the Java programming language:
MyThread(String name) {
td = new Thread(this, name);
suspended = false;
stopped = false;
td.start();
}
Thread.sleep(1000);
thread1.suspendThread();
System.out.println("\nSuspending " + thread1.td.getName());
Thread.sleep(1000);
thread1.resumeThread();
System.out.println("\nResuming " + thread1.td.getName());
Thread.sleep(1000);
thread2.suspendThread();
System.out.println("\nSuspending " + thread2.td.getName());
Thread.sleep(1000);
thread2.resumeThread();
System.out.println("\nResuming " + thread2.td.getName());
Thread.sleep(1000);
thread1.stop();
System.out.println("\nStopping " + thread1.td.getName());
Thread.sleep(1000);
thread2.stop();
System.out.println("\nStopping " + thread2.td.getName());
}
}
Output
Thread 1 starting.
Thread 2 starting.
1 2 3 4 1 2 3 5 6 7 8 9 10 4
5 6 7 8 9 10
11 11 12 13 14 15 16 17 18 19 20
12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
31 32 33 34 35 36 37 38 39 40
Suspending Thread 1
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
41 42 43 44 45 46 47 48 49 50
Resuming Thread 1
81 82 83 84 85 86 87 88 89 90
51 52 53 54 55 56 57 58 59 60
91 92 93 94 95 96 97 98 99 100
61 62 63 64 65 66 67 68 69 70
101 102 103 104 105 106 107 108 109 110
71 72 73 74 75 76 77 78 79 80
111 112 113 114 115 116 117 118 119 120
Suspending Thread 2
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110
111 112 113 114 115 116 117 118 119 120
121 122 123 124 125 126
Resuming Thread 2
127 128 129 130
121 122 123 124 125 126 127 128 129 130
131 132 133 134 135 136 137 138 139 140
131 132 133 134 135 136 137 138 139 140
141 142 143 144 145 146 147 148 149 150
141 142 143 144 145 146 147 148 149 150
151 152 153 154 155 156 157 158 159 160
151 152 153 154 155 156 157 158 159 160
Stopping Thread 1
161 162 163 164 165 166 167 168 169 170
Thread 1 exiting.
171 172 173 174 175 176 177 178 179 180
181 182 183 184 185 186 187 188 189 190
191 192 193 194 195 196 197 198 199 200
Stopping Thread 2
Thread 2 exiting.
This program creates a class called MyThread that adheres to the interface known as Runnable.
There are three ways to stop, suspend, and resume a thread in the MyThread class.
The MyThread class's run method defines the thread's behavior, which is to print numbers from 1
to 999 with a delay of 250 milliseconds after every ten numbers, until the stopped flag becomes
true. If the suspended flag is set, the thread waits by invoking the object's wait method.
The ThreadExample class instantiates two instances of the MyThread class and calls the
suspendThread, resumeThread, and stop methods on them after a second interval. The suspend
Thread method sets the suspended flag to true, causing the thread to be suspended by calling wait
on the object. To wake up the waiting thread, the resumeThread method sets the suspended flag
to false and calls notify on the object. The stopped flag is set to true by the stop method, which
terminates the thread.
Obtaining a Thread’s State:
The getState() method of thread class returns the thread's state. This method is designed for
monitoring the system state.
Syntax
public Thread.State getState() .
Example:
public class JavaGetStateExp implements Runnable
{
public void run()
{
// returns the state of the thread
Thread.State state = Thread.currentThread().getState();
System.out.println("Running thread name: "+ Thread.currentThread().getName());
System.out.println("State of thread: " + state);
}
public static void main(String args[])
{
JavaGetStateExp g = new JavaGetStateExp();
Thread t1= new Thread(g);
Thread t2= new Thread(g);
// call run() function
t1.start();
t2.start();
}
}
Output:
Running thread name: Thread-0
State of thread: RUNNABLE
Running thread name: Thread-1
State of thread: RUNNABLE
Enumeration in Java is a data type that defines a set of named constants. It is used to create a list
of values assigned to a type. This can then be used in the program.In Java, Enumerations are
represented using the "enum" keyword.
All enumerations automatically contain two predefined methods: values() and valueOf(). Their
general forms are:
public static enum-type[ ] values()
public static enum-type valueOf(String str)
The values() method returns an array that contains a list of the enumeration constants.
The valueOf() method returns the enumeration constant whose value corresponds to the string
passed in str. In both cases, enum-type is the type of the enumeration.
Ouput:
//ouput of values()
East
South
West
North
//output of valueOf()
South
Type Wrappers:
Type wrappers, or wrapper classes, in Java are used to convert primitive data types
into objects. Each primitive data type has a corresponding wrapper class. For
example, the wrapper class for the primitive type int is Integer.
Wrapper classes provide a way to use primitive data types (int, boolean, etc..) as objects.
The table below shows the primitive type and the equivalent wrapper class:
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
The Java compiler automatically converts primitive types to their corresponding objects, a
process called autoboxing. The opposite process, converting objects back to primitive types, is
called unboxing.
The Scanner class is another example of a wrapper class in Java. It encapsulates an input stream
and provides methods for reading lines and breaking them into tokens.
Sometimes you must use wrapper classes, for example when working with Collection objects,
such as ArrayList, where primitive types cannot be used (the list can only store objects):
Example :
Example
Integer myInt = 5;
System.out.println(myInt);
System.out.println(myDouble);
System.out.println(myChar);
Output:
5
5.99
A
-No need of conversion between primitives and Wrappers manually so less coding is required.
In autoboxing, the Java compiler automatically converts primitive types into their corresponding
wrapper class objects.
For example,
int a = 56;
// autoboxing
Integer aObj = a;
import java.util.ArrayList;
class Main {
public static void main(String[] args) {
//autoboxing
list.add(5);
list.add(6);
class Main {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
//autoboxing
list.add(5);
list.add(6);
// unboxing
int a = list.get(0);
System.out.println("Value at index 0: " + a);
}
}
Output
ArrayList: [5, 6]
Value at index 0: 5