Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Java Thread Creation and Lifecycle Management

Tech 2

Concurrency Foundations

Modern operating systems distinguish between processes and execution contexts. While processes represent independent memory spaces and resource allocations, threads serve as the schedulable units within those spaces. A single process may host multiple threads sharing the same memory address space, making thread creation and destruction significantly lighter than process management.

Java abstracts platform-specific thread implementations through the java.lang.Thread class, which maps one-to-one with underlying operating system threads.

Thread Instantiation Patterns

Approach 1: Class Inheritance

The most direct method involves extending Thread and overriding the run() method:

class DataProcessor extends Thread {
    @Override
    public void run() {
        System.out.println("Processing data in: " + getName());
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        DataProcessor worker = new DataProcessor();
        worker.start(); // Initiates OS-level thread execution
    }
}

Note that invoking run() directly executes the method on the current thread, where as start() requests a new system thread.

Approach 2: Interface Implementation

Decoupling the execution logic from the thread itself using Runnable:

class DownloadTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Downloading resource...");
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Thread downloader = new Thread(new DownloadTask());
        downloader.start();
    }
}

This approach promotes better separation of concerns and allows the task to be submitted to various execution frameworks beyond raw threads.

Approach 3: Anonymous Thread Subclass

For localized behavior definition:

public class ThreadDemo {
    public static void main(String[] args) {
        Thread backgroundJob = new Thread() {
            @Override
            public void run() {
                System.out.println("Anonymous thread executing");
            }
        };
        backgroundJob.start();
    }
}

Approach 4: Anonymous Runnable

Alternative anonymous implementation:

public class ThreadDemo {
    public static void main(String[] args) {
        Thread taskRunner = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable task completed");
            }
        });
        taskRunner.start();
    }
}

Approach 5: Lambda Expressions

Modern functional syntax for concise task definition:

public class ThreadDemo {
    public static void main(String[] args) {
        Thread asyncTask = new Thread(() -> {
            System.out.println("Lambda-based execution");
        });
        asyncTask.start();
    }
}

Execution Performance Characteristics

To demonstrate the potential of parallel execution, consider incrementing two separate counters:

public class PerformanceTest {
    private static final long ITERATIONS = 1_000_000_000L;
    
    public static void sequentialSum() {
        long start = System.currentTimeMillis();
        
        long sumAlpha = 0;
        for (long i = 0; i < ITERATIONS; i++) {
            sumAlpha++;
        }
        
        long sumBeta = 0;
        for (long i = 0; i < ITERATIONS; i++) {
            sumBeta++;
        }
        
        System.out.println("Sequential time: " + (System.currentTimeMillis() - start) + "ms");
    }
    
    public static void parallelSum() throws InterruptedException {
        long start = System.currentTimeMillis();
        
        Thread workerOne = new Thread(() -> {
            long accumulator = 0;
            for (long i = 0; i < ITERATIONS; i++) {
                accumulator++;
            }
        });
        
        Thread workerTwo = new Thread(() -> {
            long accumulator = 0;
            for (long i = 0; i < ITERATIONS; i++) {
                accumulator++;
            }
        });
        
        workerOne.start();
        workerTwo.start();
        workerOne.join();
        workerTwo.join();
        
        System.out.println("Parallel time: " + (System.currentTimeMillis() - start) + "ms");
    }
}

The join() method ensures the main thread waits for completion before measuring elapsed time.

Thread Identification

Assigning meaningful names aids debugging and monitoring:

Thread namedThread = new Thread(() -> {
    // Task logic
}, "Database-Connection-Pool-Monitor");

Tools like jconsole or jvisualvm display these identifiers during runtime analysis.

Cooperative Termination

Threads should terminate naturally upon run() completion. For premature interruption:

Custom Flag Approach (limited scope):

public class GracefulShutdown {
    private static volatile boolean terminateRequested = false;
    
    public static void main(String[] args) throws InterruptedException {
        Thread serviceThread = new Thread(() -> {
            while (!terminateRequested) {
                System.out.println("Service active");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        serviceThread.start();
        Thread.sleep(5000);
        terminateRequested = true;
    }
}

Standard Interruption Mechanism:

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread responsiveTask = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Working...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // Interrupted while sleeping - cleanup and exit
                    System.out.println("Interrupted during sleep");
                    break;
                }
            }
        });
        
        responsiveTask.start();
        Thread.sleep(3500);
        responsiveTask.interrupt(); // Sets flag or triggers exception if blocked
    }
}

When interrupt() is invoked:

  • If the target thread is executing normally, its interrupt status flag sets to true
  • If the target thread is in WAITING, TIMED_WAITING, or BLOCKED state (e.g., sleeping), it receives an InterruptedException and clears the interrupt flag

Thread Synchronization via Joining

To enforce completion order dependencies:

Thread phaseOne = new Thread(() -> initializeDatabase());
Thread phaseTwo = new Thread(() -> initializeCache());

phaseOne.start();
phaseOne.join(); // Blocks until phaseOne completes

phaseTwo.start();
phaseTwo.join(); // Blocks until phaseTwo completes

System.out.println("System fully initialized");

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.