Advanced Java Individual Assignment
Advanced Java Individual Assignment
College of Computing
Java Programming
Course Code: CoSC 3053
Individual Assignment
Name: Kidus Yared
ID:4109/13
Section: B
What is Thread?
Threads: a lightweight unit of execution within a program. It is a fundamental part of
Java's multithreading support, allowing concurrent execution of multiple tasks or
processes within a single program. It refers to a sequence of instructions that can be
executed independently within a program. It also refers to a separate flow of execution
within a program that can run independently of other threads. Threads allow different
parts of a program to execute concurrently, leading to improved performance,
responsiveness, and utilization of system resources.
Java threads are managed by the Java Virtual Machine (JVM) and provide features
such as synchronization, which allows threads to communicate and coordinate with
each other. Threads can be created by extending the Thread class or implementing the
Runnable interface in Java. Each Java program has at least one thread, known as the
main thread, which is automatically created when the program starts. Additional threads
can be created by the programmer to perform specific tasks concurrently. These
threads can run concurrently, meaning they can execute simultaneously, or they can be
scheduled to run at different times based on the program's requirements.
Some examples of Multithreading are Web browser, Word processor Video processor.
B. Multitasking: is the ability of a program to execute multiple tasks
concurrently. It can be achieved using threads, which are lightweight units of
execution within a program. Multitasking enables users to run multiple
applications at the same time and allows for efficient sharing of system
resources among different processes. The ability to perform multitasking in
Java allows for efficient utilization of system resources and enables
concurrent execution of multiple tasks, leading to improved performance and
responsiveness in applications.
Multitasking can be achieved by creating and starting multiple threads, each performing
a specific task or operation. By using multitasking, Java programs can perform tasks
concurrently, which can lead to improved performance, responsiveness, and utilization
of system resources. Threads can execute independently, allowing for parallelism when
running on multi-core processors.
heavyweight. The cost of communication between the processes is high. Saving and
loading of registers, memory maps, and updating lists are required to switch from one
process to another. Java supports multitasking at the process level, where multiple
processes can run concurrently on a computer system. Each process has its own
memory space and resources, and they are managed by the operating system.
It's is the utilization of two or more central processing units (CPUs) in a single computer
system. Its definition can vary depending on the context, but generally it refers to a
system's ability to support multiple CPUs and its capacity to distribute work among
them.
Process vs Thread
Process: is an active program that is under execution. It is more than the program
code, as it includes the program counter, process stack, registers, program code etc.
Compared to this, the program code is only the text section. It represents the execution
of a Java application, which can consist of multiple threads. Each Java process has its
own memory space, system resources, and a separate instance of the Java Virtual
Machine (JVM) running the application. When you run a Java program, it starts a new
process that operates independently of other processes. Each process runs
independently and is isolated from other processes, meaning that it has its own memory
space and cannot directly access or modify the memory of other processes. Processes
are managed by the operating system, which schedules their execution, assigns system
resources, and facilitates inter-process communication. Processes communicate with
each other through inter-process communication (IPC) mechanisms, such as pipes,
shared memory, or message passing.
A thread shares information like data segment, code segment, files, etc. with its peer
threads while it contains its own registers, stack, counter, etc. It’s basically a subpart of
a large process. In a process, all the threads within it are interrelated to each other. A
typical thread contains some information like data segment, code segment, etc. This
information is being shared with their peer threads during execution. The most important
feature of threads is that they share memory, data, resources, etc. with their peer
threads within a process to which they belong.
· Both processes and threads are related to each other and very much similar,
hence creating confusion to understand the differences between both of
them. The process and thread are an independent sequence of execution, but
both are differentiated in a way that processes execute in different memory
spaces, whereas threads of the same process execute in shared memory
space.
The process of multi-tasking lets a CPU execute various tasks at the very same time.
The process of multi-threading lets a CPU generate multiple threads out of a task and
process all of them simultaneously. A user can easily perform various tasks
simultaneously with their CPU using multitasking. In thread-based multitasking, the
thread is the smallest unit of dispatchable code. This means that a single program can
perform two or more tasks simultaneously. Example: A Text Editor can format text at the
same time it is printing, as long as these two actions are performed by two separate
threads.
Java's multithreading capabilities enable you to create, manage, and coordinate multiple
threads within a single program. Threads within a Java program share the same
memory space, enabling efficient communication and data sharing. However, it's crucial
to handle thread synchronization and potential race conditions when multiple threads
access shared data to ensure data integrity and avoid conflicts.
By utilizing multithreading in Java, you can effectively utilize system resources, improve
responsiveness, and achieve parallelism within your program, leading to enhanced
performance and better user experience. It is commonly used in scenarios such as
graphical user interfaces (GUIs), server applications, and computationally intensive
tasks. For example, in a GUI application, you can use separate threads to handle user
input, update the interface, and perform background computations simultaneously,
ensuring smooth user interaction. It enables you to enhance the performance and
responsiveness of your program by leveraging the capabilities of modern processors
with multiple cores or support for simultaneous multithreading (SMT). By dividing the
workload into multiple threads, you can achieve parallel execution of tasks, making
efficient use of available system resources.
Multithreading allows many parts of a program to run simultaneously. These parts are
referred to as threads, and they are lightweight processes that are available within the
process. As a result, multithreading increases CPU utilization through multitasking. By
utilizing multi-threading, Java programs can achieve parallelism and make efficient use
of available system resources, such as CPU cores. Java provides built-in support for
multi-threading through the java.lang. Thread class and the java.lang.Runnable
interface.
Multithreading enables us to run multiple threads concurrently. For instance, in a web
browser, we can have one thread that handles the user interface, and in parallel, we
can have another thread that fetches the data to be displayed. Therefore, multithreading
improves the responsiveness of a system.
Java provides synchronization mechanisms that allow threads to safely access shared
resources. The most commonly used synchronization mechanism in Java is the
synchronized keyword, which can be applied to methods or code blocks. When a thread
encounters a synchronized block or method, it acquires a lock on the associated
monitor, ensuring that only one thread can execute the synchronized code at a time.
Other threads attempting to access the same synchronized block or method will be
blocked until the lock is released. By properly synchronizing access to shared
resources, thread synchronization prevents data corruption and ensures data integrity. It
establishes a happens-before relationship, ensuring that changes made by one thread
are visible to other threads.
Thread priorities are used to provide guidance to the Java thread scheduler, which sets
the order in which threads receive CPU time. They are typically used to indicate the
relative importance or urgency of different threads within an application. Threads with
higher priorities are more likely to be scheduled and executed by the scheduler than
threads with lower priorities. It is crucial to remember, however, that thread priorities are
not strictly guaranteed and may vary among platforms and JVM implementations.
The setPriority() function of the Thread class in Java can be used to set the priority of a
thread. The getPriority() method can also be used to retrieve a thread's priority.It is
important to use thread priorities wisely and understand their limitations. While higher-
priority threads are more likely to be scheduled, strict ordering and fairness are not
guaranteed. It is generally not advisable to rely only on thread priority for crucial
synchronization or timing requirements.
Types of Synchronization
Synchronization: is the ability to regulate multiple processes’ access to a shared
resource. It’s a mechanism used to coordinate and control access to shared resources
or critical sections of code in a multi-threaded environment. Multiple threads attempt to
access shared resources at the same time under the Multithreading concept, resulting
in inconsistent outcomes. Synchronization is required for thread-to-thread
communication to be reliable. Synchronization aids in thread interference prevention. It
helps in the prevention of concurrency issues.
Synchronization is crucial for maintaining thread safety and preventing race conditions
and data corruption in multithreaded applications. By properly synchronizing access to
shared resources, developers can ensure the correct and predictable behavior of their
concurrent programs. It ensures that only one thread can access a shared resource or
execute a critical section at a time, preventing conflicts and maintaining data
consistency.
Implementation:
}public class Counter {
private int count = 0;
Example:
public class SynchronizedMethodExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
synchronized (lock) {
// Critical section
count++;
}
// Non-critical section
}
}
Example:
public class StaticSynchronizationExample {
private static int count = 0;
public static synchronized void increment() {
count++;
}
}
Ø The Object class in java contains three final methods that allow threads to
communicate about the lock status of a resource. These methods are wait(),
notify(), and notifyAll().
Example:
package com.journaldev.concurrency
public class Waiter implements Runnable{
private Message msg;
public Waiter(Message m){
this.msg=m;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (msg) {
try{
System.out.println(name+" waiting to get notified at
time:"+System.currentTimeMillis());
msg.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(name+" waiter thread got notified at
time:"+System.currentTimeMillis());
//process the message now
System.out.println(name+" processed: "+msg.getMsg());
}
}
}
Example:
package com.journaldev.concurrency;
public class Notifier implements Runnable {
private Message msg;
public Notifier(Message msg) {
this.msg = msg;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+" started");
try {
Thread.sleep(1000);
synchronized (msg) {
msg.setMsg(name+" Notifier work done");
msg.notify();
// msg.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Example:
package com.journaldev.concurrency;
public class WaitNotifyTest {
public static void main(String[] args) {
Message msg = new Message("process it");
Waiter waiter = new Waiter(msg);
new Thread(waiter,"waiter").start();
Waiter waiter1 = new Waiter(msg);
new Thread(waiter1, "waiter1").start();
Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifier").start();
System.out.println("All the threads are started");
}
}
In this example, the Message class represents a shared message object between the
Producer and Consumer threads. The produce() method is used by the producer to set
the content of the message, and the consume() method is used by the consumer to
retrieve the content of the message. The methods are synchronized and use the wait()
and notify() calls to control the execution flow. The producer waits when the message is
not empty, and the consumer waits when the message is empty. The notify() calls notify
the waiting threads to resume execution.