Java Threads

Java Threads are the smallest unit of execution within a Java program. Threads allow a program to perform multiple tasks simultaneously, resulting in more efficient use of resources and improved performance. Java provides built-in support for multi-threading through the java.lang.Thread class.

A Java program can have multiple threads of execution running concurrently. Each thread can execute a different part of the program’s code simultaneously. When multiple threads are running, they share the same memory space and can access the same variables and objects. However, this shared access can also create problems if not handled properly, such as race conditions and deadlocks.

Creating a thread in Java involves creating an instance of the Thread class and implementing the run() method. The run() method contains the code that will be executed when the thread is started. A thread can be started using the start() method of the Thread class.

Java also provides several features to manage and control threads. These include synchronization, which is used to ensure that multiple threads do not access shared resources at the same time; locks, which are used to prevent multiple threads from accessing the same resource simultaneously; and wait and notify, which are used to coordinate the activities of multiple threads.

Overall, Java Threads provide a powerful and flexible mechanism for developing multi-threaded programs in Java, allowing developers to take advantage of the benefits of parallel processing and efficient use of resources.

Thread class:

The Thread class is a built-in class in Java that represents a thread of execution within a program. It provides several methods and properties that allow developers to create, manage and control threads. Some of the important methods and properties of the Thread class are:

  • start(): This method is used to start a new thread of execution. When called, the run() method of the Thread object is executed in a separate thread.
  • run(): This method is the entry point for the thread’s code. It contains the code that will be executed when the thread is started.
  • sleep(): This method causes the current thread to sleep for a specified period of time, in milliseconds.
  • join(): This method causes the current thread to wait for the thread on which it is called to complete its execution.
  • getName() and setName(): These methods are used to get and set the name of the thread, respectively.
  • isAlive(): This method is used to determine whether a thread is still alive or has terminated.
  • interrupt(): This method is used to interrupt a thread that is currently sleeping or blocked.

The Thread class also provides several other methods and properties that allow developers to manage and control threads. It is important to note that while the Thread class provides a powerful and flexible mechanism for developing multi-threaded programs in Java, it is important to use it carefully to avoid common pitfalls such as race conditions and deadlocks.

Commonly used Constructors of Thread class:

The Thread class in Java provides several constructors that allow developers to create and initialize threads. Some commonly used constructors of the Thread class are:

  1. Thread(): This constructor creates a new thread object with default values for its properties.
  2. Thread(Runnable target): This constructor creates a new thread object with the specified Runnable object as the target of the thread.
  3. Thread(Runnable target, String name): This constructor creates a new thread object with the specified Runnable object as the target of the thread, and the specified name.
  4. Thread(String name): This constructor creates a new thread object with the specified name.
  5. Thread(ThreadGroup group, Runnable target): This constructor creates a new thread object with the specified ThreadGroup as the group for the thread, and the specified Runnable object as the target of the thread.
  6. Thread(ThreadGroup group, Runnable target, String name): This constructor creates a new thread object with the specified ThreadGroup as the group for the thread, the specified Runnable object as the target of the thread, and the specified name.
  7. Thread(ThreadGroup group, Runnable target, String name, long stackSize): This constructor creates a new thread object with the specified ThreadGroup as the group for the thread, the specified Runnable object as the target of the thread, the specified name, and the specified stack size.

It is important to note that when using the Thread class, the most commonly used constructor is Thread(Runnable target), which takes a Runnable object as an argument. This is because the Runnable interface provides a more flexible and efficient mechanism for defining the code to be executed by the thread.

Commonly used methods of Thread class:

The Thread class in Java provides several methods that allow developers to create, manage, and control threads. Some commonly used methods of the Thread class are:

  1. start(): This method is used to start a new thread of execution. When called, the run() method of the Thread object is executed in a separate thread.
  2. run(): This method is the entry point for the thread’s code. It contains the code that will be executed when the thread is started.
  3. sleep(long millis): This method causes the current thread to sleep for a specified period of time, in milliseconds.
  4. join(): This method causes the current thread to wait for the thread on which it is called to complete its execution.
  5. getName() and setName(String name): These methods are used to get and set the name of the thread, respectively.
  6. isAlive(): This method is used to determine whether a thread is still alive or has terminated.
  7. interrupt(): This method is used to interrupt a thread that is currently sleeping or blocked.
  8. yield(): This method is used to suggest to the scheduler that the current thread should yield the CPU to another thread of equal priority.
  9. setPriority(int priority) and getPriority(): These methods are used to set and get the priority of the thread, respectively.
  10. isInterrupted(): This method is used to determine whether a thread has been interrupted.
  11. currentThread(): This method is used to get a reference to the currently executing thread.
  12. setDaemon(boolean on): This method is used to set the daemon status of the thread.

These methods provide a powerful and flexible mechanism for developing multi-threaded programs in Java, allowing developers to take advantage of the benefits of parallel processing and efficient use of resources.

Runnable interface:

The Runnable interface is a built-in interface in Java that defines a single method, run(), which contains the code to be executed by a thread. This interface is commonly used to create threads in Java, by defining a class that implements the Runnable interface and passing an instance of that class to the constructor of the Thread class.

To implement the Runnable interface, a class must provide an implementation of the run() method, which contains the code to be executed when the thread is started. For example:

public class MyRunnable implements Runnable {
    public void run() {
        // Code to be executed by the thread goes here
    }
}

Once a class has been defined that implements the Runnable interface, an instance of that class can be passed to the constructor of the Thread class, as shown below:

MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();

When the start() method of the Thread object is called, the run() method of the MyRunnable object will be executed in a separate thread of execution.

Using the Runnable interface provides a more flexible and efficient mechanism for defining the code to be executed by a thread, compared to extending the Thread class directly. This is because a class can implement multiple interfaces, whereas it can only extend a single class. Additionally, by separating the code to be executed from the management of the thread, it is easier to design and maintain multi-threaded programs that are free from common pitfalls such as race conditions and deadlocks.

Starting a thread:

In Java, a new thread can be started by creating a Thread object and calling its start() method. When the start() method is called, a new thread of execution is created, and the run() method of the Thread object is executed in that new thread. Here’s an example:

Thread myThread = new Thread() {
    public void run() {
        // Code to be executed in the new thread goes here
    }
};
myThread.start();

In this example, a new Thread object is created, with the run() method containing the code to be executed in the new thread. The start() method is then called to start the new thread.

Alternatively, a new thread can also be started by creating a class that implements the Runnable interface, and passing an instance of that class to the Thread constructor. Here’s an example:

class MyRunnable implements Runnable {
    public void run() {
        // Code to be executed in the new thread goes here
    }
}

Thread myThread = new Thread(new MyRunnable());
myThread.start();

In this example, a new class MyRunnable is defined that implements the Runnable interface, with the run() method containing the code to be executed in the new thread. An instance of MyRunnable is then passed to the constructor of the Thread class, and the start() method is called to start the new thread.

It is important to note that calling the start() method on a Thread object does not immediately start the thread. Instead, it schedules the thread to be started by the JVM, and returns control to the calling thread. Once the JVM starts the new thread, the run() method of the Thread object is executed in the new thread.

1) Java Thread Example by extending Thread class:

Sure, here’s an example of creating a thread in Java by extending the Thread class:

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread " + Thread.currentThread().getId() + " is running.");
    }
}

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            MyThread thread = new MyThread();
            thread.start();
        }
    }
}

In this example, we create a new class MyThread that extends the Thread class and overrides its run() method with a custom implementation. The run() method simply prints a message to the console indicating that the thread is running.

In the main() method, we create five instances of the MyThread class and start each of them using the start() method. When the start() method is called, a new thread of execution is created, and the run() method of the MyThread object is executed in that new thread.

When we run the Main class, we should see output similar to the following:

Thread 8 is running.
Thread 9 is running.
Thread 10 is running.
Thread 11 is running.
Thread 12 is running.

Note that the exact thread IDs may be different each time the program is run, as they are assigned dynamically by the JVM.

2) Java Thread Example by implementing Runnable interface:

Sure, here’s an example of creating a thread in Java by implementing the Runnable interface:

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Thread " + Thread.currentThread().getId() + " is running.");
    }
}

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new MyRunnable());
            thread.start();
        }
    }
}

In this example, we create a new class MyRunnable that implements the Runnable interface and overrides its run() method with a custom implementation. The run() method simply prints a message to the console indicating that the thread is running.

In the main() method, we create five instances of the Thread class, passing in a new instance of the MyRunnable class as a constructor argument. We then start each of the Thread objects using the start() method. When the start() method is called, a new thread of execution is created, and the run() method of the MyRunnable object is executed in that new thread.

When we run the Main class, we should see output similar to the following:

Thread 8 is running.
Thread 9 is running.
Thread 10 is running.
Thread 11 is running.
Thread 12 is running.

Note that the exact thread IDs may be different each time the program is run, as they are assigned dynamically by the JVM.

3) Using the Thread Class: Thread(String Name):

The Thread class in Java provides several constructors to create and initialize threads. One of these constructors is Thread(String name), which creates a new thread with a specified name.

Here’s an example that demonstrates how to use the Thread(String name) constructor:

class MyThread extends Thread {
    public MyThread(String name) {
        super(name);
    }
    
    public void run() {
        System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
    }
}

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            MyThread thread = new MyThread("Thread " + i);
            thread.start();
        }
    }
}

In this example, we create a new class MyThread that extends the Thread class and overrides its run() method with a custom implementation. The MyThread class also has a constructor that takes a String parameter, which is used to set the name of the thread using the setName() method of the Thread class.

In the main() method, we create five instances of the MyThread class, passing in a string that specifies the name of the thread. We then start each of the MyThread objects using the start() method. When the start() method is called, a new thread of execution is created, and the run() method of the MyThread object is executed in that new thread.

When we run the Main class, we should see output similar to the following:

Thread 0 is running.
Thread 2 is running.
Thread 3 is running.
Thread 4 is running.
Thread 1 is running.

Note that the exact order of thread execution may be different each time the program is run, as it depends on the thread scheduler algorithm used by the JVM. However, the names of the threads should be consistent with the names specified in the constructor.

4) Using the Thread Class: Thread(Runnable r, String name):

The Thread class in Java provides another constructor that takes a Runnable object and a String name as arguments. This constructor creates a new thread with the specified name, which executes the run() method of the Runnable object.

Here’s an example that demonstrates how to use the Thread(Runnable r, String name) constructor:

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
    }
}

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new MyRunnable(), "Thread " + i);
            thread.start();
        }
    }
}

In this example, we create a new class MyRunnable that implements the Runnable interface and overrides its run() method with a custom implementation.

In the main() method, we create five instances of the Thread class, passing in a new instance of the MyRunnable class and a string that specifies the name of the thread. We then start each of the Thread objects using the start() method. When the start() method is called, a new thread of execution is created, and the run() method of the MyRunnable object is executed in that new thread.

When we run the Main class, we should see output similar to the following:

Thread 0 is running.
Thread 1 is running.
Thread 3 is running.
Thread 2 is running.
Thread 4 is running.

Note that the exact order of thread execution may be different each time the program is run, as it depends on the thread scheduler algorithm used by the JVM. However, the names of the threads should be consistent with the names specified in the constructor.