Java Multithreading Fundamentals and Implementation
Multithreading in Java allows concurrent execuiton of two or more parts of a program for maximum utilization of CPU. Each part of such a program is called a thread, and each thread defines a separate path of execution.
Below we explore different ways to implement multithreading in Java:
Implementing Threads by Extending Thread Class
One way to create a thread in Java is by extending the Thread class and overriding its run() method. Here's an example:
package concurrency;
public class AudioPlayer extends Thread { @Override public void run() { for(int counter = 0; counter < 1000; counter++) try { Thread.sleep(100); System.out.println("Playing audio"); } catch (InterruptedException e) { e.printStackTrace(); } } }
</div><div>```
package concurrency;
public class FoodConsumer extends Thread {
@Override
public void run() {
for(int counter = 0; counter < 1000; counter++)
try {
Thread.sleep(100);
System.out.println("Eating food");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package concurrency;
public class ThreadDemo { /** * Audio playing method */ private static void playAudio() { for(int counter = 0; counter < 1000; counter++) System.out.println("Playing audio"); }
/**
* Food consuming method
*/
private static void consumeFood() {
for(int counter = 0; counter < 1000; counter++)
System.out.println("Eating food");
}
public static void main(String[] args) {
// Using multithreading to perform both tasks concurrently
AudioPlayer audioThread = new AudioPlayer();
FoodConsumer foodThread = new FoodConsumer();
audioThread.start();
foodThread.start();
}
}
</div>The above code demonstrates how multithreading enables simultaneous execution of playing audio and consuming food tasks.
### Implementing Threads Using Runnable Interface
Another approach to create threads is by implementing the Runnable interface. This method is more flexible as it allows the class to extend another class:
<div>```
package concurrency;
public class TaskRunner implements Runnable {
@Override
public void run() {
for(int counter = 0; counter < 10; counter++) {
// Get current thread
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() + ": " + counter); // Display thread name
}
}
public static void main(String[] args) {
TaskRunner taskRunner = new TaskRunner();
Thread thread1 = new Thread(taskRunner);
System.out.println("Is thread1 active: " + thread1.isAlive());
thread1.start();
// Check if thread is in active state
System.out.println("Is thread1 active: " + thread1.isAlive());
}
}
Setting Thread Priorities
Java allows setting thread priorities to influence thread scheduling. Threads with higher priorities are generally executed before threads with lower priorities:
package concurrency;
public class PriorityDemo implements Runnable {
@Override
public void run() {
for(int counter = 0; counter < 10; counter++) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() + ": " + counter);
}
}
public static void main(String[] args) {
PriorityDemo priorityDemo = new PriorityDemo();
Thread highPriority = new Thread(priorityDemo, "High");
Thread lowPriority = new Thread(priorityDemo, "Low");
Thread normalPriority = new Thread(priorityDemo, "Normal");
highPriority.setPriority(Thread.MAX_PRIORITY);
lowPriority.setPriority(Thread.MIN_PRIORITY);
normalPriority.setPriority(Thread.NORM_PRIORITY);
highPriority.start();
lowPriority.start();
normalPriority.start();
}
}
</div>In this example, the thread named "High" has maximum priority, "Normal" has medium priority, and "Low" has minimum priority.
### Thread Yielding
The yield() method in Java causes the currently executing thread to pause temporarily and allow other threads to execute:
<div>```
package concurrency;
public class YieldDemo implements Runnable {
@Override
public void run() {
for(int counter = 0; counter < 10; counter++) {
try {
Thread.sleep(100);
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() + ": " + counter);
if(counter == 5) {
System.out.println("Thread yielding:");
Thread.currentThread().yield();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
YieldDemo yieldDemo = new YieldDemo();
new Thread(yieldDemo, "Process A").start();
new Thread(yieldDemo, "Process B").start();
}
}