Java实现多线程的n种方法

 更新时间:2024年11月26日 09:37:06   作者:繁依Fanyi  
在现代编程中,多线程是一项关键技术,它使得程序能够同时执行多个任务,提高了系统的效率和性能,在Java中,有多种方法可以实现多线程,本文将详细介绍几种常见的Java多线程实现方法,需要的朋友可以参考下

Java 多线程实现的多种方法

在现代编程中,多线程是一项关键技术,它使得程序能够同时执行多个任务,提高了系统的效率和性能。在Java中,有多种方法可以实现多线程,每种方法都有其独特的应用场景和优缺点。本文将详细介绍几种常见的Java多线程实现方法,包括基础的Thread类、Runnable接口、高级的线程池、并发工具类、异步编程以及新的并发特性,帮助你深入理解多线程的不同实现方式。

1. Java多线程基础概念

什么是线程?

线程是操作系统中最小的执行单元。它包含了程序执行的顺序、调用栈、寄存器等资源。一个进程可以包含多个线程,每个线程共享进程的资源(如内存、文件句柄等),但有自己的独立执行路径。

为什么要使用多线程?

多线程允许程序同时执行多个任务,从而最大化利用多核处理器的能力,提高程序的执行效率。例如,GUI应用程序可以在一个线程中处理用户输入,同时在另一个线程中执行耗时的计算,避免界面卡顿。

Java中的线程模型

Java中的线程是基于操作系统的原生线程实现的,Java提供了java.lang.Thread类和java.lang.Runnable接口来支持多线程编程。Java 5及以后引入了更高级的并发工具,如Executor框架、并发工具类和异步编程模型,这些工具极大地简化了多线程编程的复杂性。

2. 继承Thread类

最基础的实现多线程的方法之一是继承Thread类。通过继承Thread类,可以直接使用类中的start()方法来启动线程。

实现方式

class MyThread extends Thread {
    @Override
    public void run() {
        // 线程执行的代码
        System.out.println("Thread is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();  // 启动线程
    }
}

run()方法中包含了线程的执行逻辑。start()方法会创建新线程,并自动调用run()方法。

适用场景

继承Thread类的方法适用于简单的多线程实现,特别是当每个线程都是独立的任务时。

优缺点

  • 优点:

    • 实现简单,直接继承Thread类并重写run()方法即可。
  • 缺点:

    • Java只允许单继承,如果已经继承了其他类,则无法继承Thread类。
    • 不适合复杂的多线程管理场景,如线程池管理。

3. 实现Runnable接口

另一个实现多线程的基本方法是实现Runnable接口。与继承Thread类不同,实现Runnable接口更灵活,因为它允许类继承其他类。

实现方式

class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程执行的代码
        System.out.println("Runnable is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();  // 启动线程
    }
}

适用场景

实现Runnable接口适用于需要实现多线程功能但不希望受限于Java单继承机制的场景。它更适合将业务逻辑与线程控制分离的设计。

优缺点

  • 优点:

    • 可以通过实现接口实现多线程,不受Java单继承机制的限制。
    • 代码更具可重用性,业务逻辑和线程控制分离。
  • 缺点:

    • 与继承Thread类相比,启动线程需要额外创建Thread对象。

4. Callable和Future

Runnable接口的run()方法无法返回结果,也无法抛出异常。如果需要线程返回结果或抛出异常,可以使用Callable接口与Future结合使用。

介绍Callable接口

Callable接口是Java 5引入的一个功能更强的接口,它允许在执行完任务后返回结果,并且可以抛出异常。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 线程执行的代码
        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);
        thread.start();

        try {
            // 获取线程返回的结果
            Integer result = futureTask.get();
            System.out.println("Thread result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Future的作用与实现

Future接口用来表示异步计算的结果。通过调用get()方法,可以等待计算完成并获取结果。

应用场景

当线程需要返回计算结果,或在执行过程中可能抛出异常时,CallableFuture是理想的选择。

5. 使用Executor框架

在Java 5之前,开发者只能通过Thread类或Runnable接口手动管理线程。随着并发需求的增长,Java 5引入了Executor框架,极大简化了线程管理。

线程池的概念

线程池是一组可重用的线程。通过线程池,可以避免频繁创建和销毁线程,提高性能。线程池还能帮助管理并发线程的数量,防止过多线程导致系统资源耗尽。

Executors类的使用

Executors类提供了多种方法来创建线程池,例如newFixedThreadPool()newCachedThreadPool()newSingleThreadExecutor()

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

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

        executor.shutdown();
    }
}

class RunnableTask implements Runnable {
    private int taskId;

    public RunnableTask(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println("Task ID: " + this.taskId + " performed by " + Thread.currentThread().getName());
    }
}

自定义线程池

如果需要更灵活的线程池配置,可以使用ThreadPoolExecutor类自定义线程池。

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

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

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

        executor.shutdown();
    }
}

适用场景

线程池适用于高并发场景,可以有效管理和复用线程,避免频繁创建和销毁线程的开销。

6. 并发工具类的使用

Java的并发包(java.util.concurrent)中提供了许多用于线程同步和协调的工具类。以下是几种常用的工具类。

CountDownLatch

CountDownLatch用于多个线程等待某个事件完成。

import java.util.concurrent.CountDownLatch;

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

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " is working...");
                latch.countDown(); // 每个线程完成后调用countDown()
            }).start();
        }

        latch.await(); // 等待所有线程完成
        System.out.println("All threads have finished.");
    }
}

CyclicBarrier

CyclicBarrier用于多个线程相互等待,直到所有线程到达屏障(Barrier)时再继续执行。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

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

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

Semaphore

Semaphore用于控制同时访问特定资源的线程数量。

import java.util.concurrent.Semaphore;

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

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println(Thread.currentThread().getName() + " is performing a task.");
                    Thread.sleep(2000);
                    semaphore.release(); // 释放许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Exchanger

Exchanger用于在两个线程之间交换数据。

import java.util.concurrent.Exchanger;

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 = exchanger.exchange(data);
                System.out.println("Thread A received: " + receivedData);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

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

Phaser

PhaserCyclicBarrier类似,但它更灵活,允许线程动态参与或离开。

import java.util.concurrent.Phaser;

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

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " is in phase " + phaser.getPhase());
                phaser.arriveAndAwaitAdvance(); // 到达并等待其他线程
            }).start();
        }

        phaser.arriveAndDeregister(); // 主线程离开,其他线程可继续进行
        System.out.println("Main thread is deregistered from the phaser.");
    }
}

适用场景

这些工具类适用于需要多个线程协同工作的场景,可以帮助开发者简化线程同步和协调逻辑。

7. Lock和Condition的使用

在Java 5之前,开发者只能使用synchronized关键字来实现线程同步。Java 5引入了Lock接口,提供了更灵活的锁机制。

ReentrantLock

ReentrantLockLock接口的一个常用实现,支持重入锁特性,允许线程重复获取锁而不发生死锁。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

    public void performTask() {
        lock.lock(); // 获取锁
        try {
            // 执行任务
            System.out.println(Thread.currentThread().getName() + " is performing a task.");
        } finally {
            lock.unlock(); // 释放锁
        }
    }

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

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

Condition

Condition接口提供了比synchronizedwait/notify机制更灵活的线程间通信方式。通过Condition,可以实现更复杂的等待/通知模式。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

    public void performTask() throws InterruptedException {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " is waiting.");
            condition.await(); // 等待信号
            System.out.println(Thread.currentThread().getName() + " is performing a task.");
        } finally {
            lock.unlock();
        }
    }

    public void signalTask() {
        lock.lock();
        try {
            System.out.println("Signal to perform the task.");
            condition.signal(); // 发送信号
        } finally {
            lock.unlock();
        }
    }

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

        new Thread(() -> {
            try {
                main.performTask();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        Thread.sleep(1000);

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

适用场景

LockCondition适用于需要更灵活的线程控制和通信的场景,例如复杂的多线程同步、等待和通知机制。

8. 使用Fork/Join框架

Fork/Join框架是Java 7引入的,用于并行执行任务。它是一个支持工作窃取(work-stealing)算法的框架,适合用于可以被递归分解的任务。

ForkJoinPool和ForkJoinTask

ForkJoinPoolFork/Join框架的核心,负责管理线程和任务。ForkJoinTask是所有任务的基类。

import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

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

class SumTask extends RecursiveTask<Long> {
    private final int start;
    private final int end;

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

    @Override
    protected Long compute() {
        if (end - start <= 10) {
            long sum = 0;
            for (int i = start; i <= 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);
            leftTask.fork(); // 执行子任务
            return rightTask.compute() + leftTask.join(); // 合并结果
        }
    }
}
适用场景

Fork/Join框架适用于需要并行执行的递归任务,例如大规模数据的处理和计算。

优缺点
  • 优点:

    • 利用工作窃取算法,可以最大化地利用多核处理器的性能。
  • 缺点:

    • 适用于特定类型的任务(如可以分解的任务),不适合所有场景。

9. 使用CompletableFuture实现异步编程

CompletableFuture是Java 8引入的类,它极大简化了异步编程,使得开发者可以以声明式的方式编写异步代码。

简介CompletableFuture

CompletableFuture支持创建、组合、等待多个异步

任务,支持链式操作,使代码更简洁。

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println("Async task is running.");
        });

        future.thenRun(() -> System.out.println("Async task finished."));

        Thread.sleep(2000); // 等待异步任务完成
    }
}

组合多个异步任务

可以使用thenCombinethenAcceptBoth等方法组合多个异步任务的结果。

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            return 10;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            return 20;
        });

        CompletableFuture<Integer> result = future1.thenCombine(future2, (x, y) -> x + y);

        result.thenAccept(sum -> System.out.println("Sum: " + sum));

        Thread.sleep(2000); // 等待异步任务完成
    }
}

处理异步计算的结果

可以使用thenApplythenAccept等方法处理异步计算的结果。

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 10;
        });

        future.thenApply(result -> result * 2)
              .thenAccept(finalResult -> System.out.println("Final Result: " + finalResult));

        Thread.sleep(2000); // 等待异步任务完成
    }
}

适用场景

CompletableFuture适用于需要处理复杂异步流程的场景,例如并发处理多个独立任务,并将结果组合成最终输出。

结论

Java 提供了多种实现多线程的方法,每种方法都有其特定的应用场景和优缺点。开发者在实际项目中,应根据需求选择合适的实现方式,并遵循多线程编程的最佳实践,以确保程序的稳定性和性能。

通过掌握这些多线程实现方式,开发者可以在高并发环境中开发出高效、可靠的应用程序。在未来的开发中,随着硬件性能的不断提升和多核处理器的普及,掌握并发编程将成为每一个Java开发者的必备技能。

以上就是Java实现多线程的n种方法的详细内容,更多关于Java实现多线程的资料请关注脚本之家其它相关文章!

相关文章

  • Java+Selenium设置元素等待的方法详解

    Java+Selenium设置元素等待的方法详解

    本文主要介绍如何使用java代码利用Selenium操作浏览器,某些网页元素加载慢,如何操作元素就会把找不到元素的异常,此时需要设置元素等待,等待元素加载完,再操作,感兴趣的可以了解一下
    2023-01-01
  • java可变参数使用示例

    java可变参数使用示例

    这篇文章主要介绍了java可变参数使用示例,需要的朋友可以参考下
    2014-04-04
  • Spring中的BeanFactory对象实例化工厂详解

    Spring中的BeanFactory对象实例化工厂详解

    这篇文章主要介绍了Spring中的BeanFactory对象实例化工厂详解,BeanFactory及其子类是Spring IOC容器中最重要的一个类,BeanFactory由类名可以看出其是一个Bean工厂类,其实它确实是一个Bean工厂类,完成Bean的初始化操作,需要的朋友可以参考下
    2023-12-12
  • 浅谈Java中Int、Integer、Integer.valueOf()、new Integer()之间的区别

    浅谈Java中Int、Integer、Integer.valueOf()、new Integer()之间的区别

    本文主要介绍了浅谈Java中Int、Integer、Integer.valueOf()、new Integer()之间的区别,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • BeanUtils.copyProperties()所有的空值不复制问题

    BeanUtils.copyProperties()所有的空值不复制问题

    这篇文章主要介绍了BeanUtils.copyProperties()所有的空值不复制问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • MyBatis的9种动态标签详解

    MyBatis的9种动态标签详解

    大家好,本篇文章主要讲的是MyBatis的9种动态标签详解,感兴趣的同学赶快来看一看吧,感兴趣的同学赶快来看一看吧
    2021-12-12
  • Java多态(动力节点Java学院整理)

    Java多态(动力节点Java学院整理)

    多态是指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。接下来通过本文给大家介绍java多态相关知识,感兴趣的朋友一起学习吧
    2017-04-04
  • 浅析Spring boot 中 logback 配置<springProperty> 读取application.properties 中的属性

    浅析Spring boot 中 logback 配置<springPropert

    这篇文章主要介绍了浅析Spring boot 中 logback 配置<springProperty> 读取application.properties 中的属性,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • springboot+VUE前后端分离实现疫情防疫平台JAVA

    springboot+VUE前后端分离实现疫情防疫平台JAVA

    本文主要使用了Java、springmvc、VUE、node.js、mybatis、mysql、tomcat、jquery、layui、bootstarp、JavaScript、html、css、jsp、log4j等一些常见的基本技术,实现一个疫情防疫小平台
    2021-08-08
  • Java实现IP地址到二进制的转换

    Java实现IP地址到二进制的转换

    这篇文章主要为大家详细介绍了Java实现IP地址到二进制的转换,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11

最新评论