SoFunction
Updated on 2025-04-11

N methods for implementing multi-threading in Java

Multiple methods of Java multithreading implementation

In modern programming, multithreading is a key technology that enables programs to perform multiple tasks at the same time, improving the efficiency and performance of the system. In Java, there are many ways to implement multi-threading, each with its unique application scenarios and advantages and disadvantages. This article will introduce several common Java multithreading methods in detail, including the basic Thread class, Runnable interface, advanced thread pool, concurrency tool classes, asynchronous programming and new concurrency features, to help you deeply understand the different implementation methods of multithreading.

1. Java multithreading basic concept

What is a thread?

Threads are the smallest execution unit in the operating system. It contains the order of program execution, call stack, register and other resources. A process can contain multiple threads, each thread shares the process's resources (such as memory, file handles, etc.), but has its own independent execution path.

Why use multithreading?

Multithreading allows programs to execute multiple tasks at the same time, thereby maximizing the ability of multi-core processors and improving program execution efficiency. For example, a GUI application can process user input in one thread while performing time-consuming calculations in another thread to avoid interface lag.

Threading model in Java

Threads in Java are implemented based on native threads of the operating system, and Java providesClass andInterface to support multi-threaded programming. Java 5 and later introduced more advanced concurrency tools such as the Executor framework, concurrent tool classes and asynchronous programming models, which greatly simplify the complexity of multithreaded programming.

2. Inherit the Thread class

One of the most basic ways to implement multithreading is inheritanceThreadkind. Through inheritanceThreadclass, you can use the class directlystart()Method to start the thread.

Implementation method

class MyThread extends Thread {
    @Override
    public void run() {
        // Code executed by thread        ("Thread is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        ();  // Start the thread    }
}

run()The method contains the execution logic of the thread.start()The method will create a new thread and will be called automaticallyrun()method.

Applicable scenarios

inheritThreadClass methods are suitable for simple multithreaded implementations, especially when each thread is an independent task.

Pros and cons

  • advantage:

    • Simple implementation, direct inheritanceThreadClass and rewriterun()Just the method.
  • shortcoming:

    • Java only allows single inheritance. If other classes have been inherited, it cannot be inherited.Threadkind.
    • Not suitable for complex multithreaded management scenarios, such as thread pool management.

3. Implement the Runnable interface

Another basic method to implement multithreading is to implementRunnableinterface. and inheritanceThreadDifferent classes, implementationRunnableThe interface is more flexible because it allows classes to inherit other classes.

Implementation method

class MyRunnable implements Runnable {
    @Override
    public void run() {
        // Code executed by thread        ("Runnable is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        ();  // Start the thread    }
}

Applicable scenarios

accomplishRunnableInterfaces are suitable for scenarios where multi-threading functionality is required but do not want to be limited by the Java single inheritance mechanism. It is more suitable for designs that separate business logic from thread control.

Pros and cons

  • advantage:

    • Multithreading can be implemented through implementation interfaces, and is not restricted by the Java single inheritance mechanism.
    • The code is more reusable, and business logic and thread control are separated.
  • shortcoming:

    • and inheritanceThreadCompared to the class, the startup thread needs to be created.ThreadObject.

4. Callable and Future

The run() method of the Runnable interface cannot return the result and cannot throw an exception. If you need a thread to return a result or throw an exception, you can use the Callable interface in conjunction with Future.

Introducing the Callable interface

The Callable interface is a more powerful interface introduced by Java 5. It allows the result to be returned after the task is executed and exceptions can be thrown.

import ;
import ;
import ;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // Code executed by thread        return 123;
    }
}

public class Main {
    public static void main(String[] args) {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        ();

        try {
            // Get the result returned by the thread            Integer result = ();
            ("Thread result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            ();
        }
    }
}

The role and implementation of Future

FutureInterfaces are used to represent the results of asynchronous calculations. By callingget()Method, you can wait for the calculation to complete and get the result.

Application scenarios

When the thread needs to return the calculation result, or an exception may be thrown during execution,CallableandFutureIdeal for you.

5. Use the Executor framework

Before Java 5, developers could only passThreadClass orRunnableThe interface manages threads manually. As concurrency demand grows, Java 5 introducesExecutorFramework greatly simplifies thread management.

The concept of thread pool

A thread pool is a set of reusable threads. Through thread pooling, frequent creation and destruction of threads can be avoided and performance can be improved. Thread pools can also help manage the number of concurrent threads and prevent excessive threads from causing system resources to be exhausted.

Use of Executors class

ExecutorsClasses provide multiple ways to create thread pools, e.g.newFixedThreadPool()newCachedThreadPool()andnewSingleThreadExecutor()

import ;
import ;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = (3);

        for (int i = 0; i < 5; i++) {
            (new RunnableTask(i));
        }

        ();
    }
}

class RunnableTask implements Runnable {
    private int taskId;

    public RunnableTask(int taskId) {
         = taskId;
    }

    @Override
    public void run() {
        ("Task ID: " +  + " performed by " + ().getName());
    }
}

Custom thread pool

If you need more flexible thread pool configuration, you can useThreadPoolExecutorClass custom thread pool.

import ;
import ;
import ;

public class Main {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 4, 60, , new LinkedBlockingQueue<>(10));

        for (int i = 0; i < 10; i++) {
            (new RunnableTask(i));
        }

        ();
    }
}

Applicable scenarios

Thread pools are suitable for high concurrency scenarios, and can effectively manage and reuse threads, avoiding the overhead of frequent creation and destruction of threads.

6. Use of concurrent tool classes

Java concurrency package () provides many tool classes for thread synchronization and coordination. The following are several commonly used tool classes.

CountDownLatch

CountDownLatchUsed for multiple threads to wait for an event to complete.

import ;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);

        for (int i = 0; i &lt; 3; i++) {
            new Thread(() -&gt; {
                (().getName() + " is working...");
                (); // Call countDown() after each thread is completed            }).start();
        }

        (); // Wait for all threads to complete        ("All threads have finished.");
    }
}

CyclicBarrier

CyclicBarrierUsed to wait for multiple threads to continue execution until all threads reach the Barrier.

import ;
import ;

public class Main {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            ("All parties have arrived at the barrier, let's proceed.");
        });

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                (().getName() + " is waiting at the barrier.");
                try {
                    ();
                } catch (InterruptedException | BrokenBarrierException e) {
                    ();
                }
                (().getName() + " has crossed the barrier.");
            }).start();
        }
    }
}

Semaphore

SemaphoreUsed to control the number of threads that access a specific resource simultaneously.

import ;

public class Main {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);

        for (int i = 0; i &lt; 5; i++) {
            new Thread(() -&gt; {
                try {
                    (); // Obtain a license                    (().getName() + " is performing a task.");
                    (2000);
                    (); // Release the license                } catch (InterruptedException e) {
                    ();
                }
            }).start();
        }
    }
}

Exchanger

ExchangerUsed to exchange data between two threads.

import ;

public class Main {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

        new Thread(() -> {
            try {
                String data = "Data from Thread A";
                String receivedData = (data);
                ("Thread A received: " + receivedData);
            } catch (InterruptedException e) {
                ();
            }
        }).start();

        new Thread(() -> {
            try {
                String data = "Data from Thread B";
                String receivedData = (data);
                ("Thread B received: " + receivedData);
            } catch (InterruptedException e) {
                ();
            }
        }).start();
    }
}

Phaser

PhaserandCyclicBarrierSimilar, but it is more flexible, allowing threads to participate dynamically or leave.

import ;

public class Main {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3);

        for (int i = 0; i &lt; 3; i++) {
            new Thread(() -&gt; {
                (().getName() + " is in phase " + ());
                (); // Arrive and wait for other threads            }).start();
        }

        (); // The main thread leaves, and other threads can continue        ("Main thread is deregistered from the phaser.");
    }
}

Applicable scenarios

These tool classes are suitable for scenarios where multiple threads work together, and can help developers simplify thread synchronization and coordination logic.

7. Use of Lock and Condition

Before Java 5, developers could only use itsynchronizedKeywords to implement thread synchronization. Java 5 has been introducedLockThe interface provides a more flexible locking mechanism.

ReentrantLock

ReentrantLockyesLockA commonly used implementation of the interface, supports the reentry lock feature, allowing threads to repeatedly acquire locks without deadlocks.

import ;
import ;

public class Main {
    private final Lock lock = new ReentrantLock();

    public void performTask() {
        (); // Get the lock        try {
            // Execute tasks            (().getName() + " is performing a task.");
        } finally {
            (); // Release the lock        }
    }

    public static void main(String[] args) {
        Main main = new Main();

        for (int i = 0; i &lt; 3; i++) {
            new Thread(main::performTask).start();
        }
    }
}

Condition

ConditionThe interface provides a comparisonsynchronizedandwait/notifyA more flexible inter-thread communication method. passCondition, more complex waiting/notification mode can be implemented.

import ;
import ;
import ;

public class Main {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = ();

    public void performTask() throws InterruptedException {
        ();
        try {
            (().getName() + " is waiting.");
            (); // Wait for signal            (().getName() + " is performing a task.");
        } finally {
            ();
        }
    }

    public void signalTask() {
        ();
        try {
            ("Signal to perform the task.");
            (); // Send signal        } finally {
            ();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();

        new Thread(() -&gt; {
            try {
                ();
            } catch (InterruptedException e) {
                ();
            }
        }).start();

        (1000);

        new Thread(main::signalTask).start();
    }
}

Applicable scenarios

LockandConditionSuitable for scenarios where more flexible thread control and communication is required, such as complex multithreaded synchronization, waiting and notification mechanisms.

8. Use the Fork/Join framework

Fork/JoinThe framework was introduced in Java 7 and is used to execute tasks in parallel. It is a framework that supports work-stealing algorithms and is suitable for tasks that can be decomposed recursively.

ForkJoinPool and ForkJoinTask

ForkJoinPoolyesFork/JoinThe core of the framework, responsible for managing threads and tasks.ForkJoinTaskis the base class for all tasks.

import ;
import ;

public class Main {
    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        long result = (new SumTask(1, 100));
        ("Sum from 1 to 100: " + result);
    }
}

class SumTask extends RecursiveTask&lt;Long&gt; {
    private final int start;
    private final int end;

    public SumTask(int start, int end) {
         = start;
         = end;
    }

    @Override
    protected Long compute() {
        if (end - start &lt;= 10) {
            long sum = 0;
            for (int i = start; i &lt;= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            int middle = (start + end) / 2;
            SumTask leftTask = new SumTask(start, middle);
            SumTask rightTask = new SumTask(middle + 1, end);
            (); // Perform subtasks            return () + (); // Merge results        }
    }
}
Applicable scenarios

Fork/JoinThe framework is suitable for recursive tasks that require parallel execution, such as processing and computing of large-scale data.

Pros and cons
  • advantage:

    • Using work-stealing algorithms, the performance of multi-core processors can be maximized.
  • shortcoming:

    • Suitable for specific types of tasks (such as tasks that can be broken down), not suitable for all scenarios.

9. Implement asynchronous programming using CompleteFuture

CompletableFutureIt is a class introduced in Java 8. It greatly simplifies asynchronous programming, allowing developers to write asynchronous code in a declarative manner.

Introduction to CompletableFuture

CompletableFutureSupports creation, combination, and waiting for multiple asynchronous

Tasks support chain operations to make the code more concise.

import ;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture&lt;Void&gt; future = (() -&gt; {
            ("Async task is running.");
        });

        (() -&gt; ("Async task finished."));

        (2000); // Wait for the asynchronous task to complete    }
}

Combining multiple asynchronous tasks

AvailablethenCombinethenAcceptBothThe results of combining multiple asynchronous tasks are achieved by other methods.

import ;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture&lt;Integer&gt; future1 = (() -&gt; {
            return 10;
        });

        CompletableFuture&lt;Integer&gt; future2 = (() -&gt; {
            return 20;
        });

        CompletableFuture&lt;Integer&gt; result = (future2, (x, y) -&gt; x + y);

        (sum -&gt; ("Sum: " + sum));

        (2000); // Wait for the asynchronous task to complete    }
}

Process the results of asynchronous calculations

AvailablethenApplythenAcceptetc. Process the results of asynchronous calculations.

import ;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture&lt;Integer&gt; future = (() -&gt; {
            return 10;
        });

        (result -&gt; result * 2)
              .thenAccept(finalResult -&gt; ("Final Result: " + finalResult));

        (2000); // Wait for the asynchronous task to complete    }
}

Applicable scenarios

CompletableFutureSuitable for scenarios where complex asynchronous processes are required, such as processing multiple independent tasks concurrently and combining the results into the final output.

in conclusion

Java provides a variety of methods to implement multi-threading, each method has its own specific application scenarios and advantages and disadvantages. In actual projects, developers should choose the appropriate implementation method according to their needs and follow the best practices of multi-threaded programming to ensure the stability and performance of the program.

By mastering these multi-threaded implementations, developers can develop efficient and reliable applications in high-concurrency environments. In future development, with the continuous improvement of hardware performance and the popularization of multi-core processors, mastering concurrent programming will become a necessary skill for every Java developer.

The above is the detailed content of n methods for Java to implement multi-threading. For more information about Java to implement multi-threading, please pay attention to my other related articles!