【多线程】Java中多线程的几种实现方式

2023-12-13 04:16:33

????????

????????多线程(multithreading)是指在一个程序中同时执行多个不同的线程(thread),每个线程都是程序的一部分,是独立的执行路径。相比于单线程程序,多线程程序可以更充分地利用计算机的多核心或多处理器资源,提高程序的运行效率和用户体验。在多线程编程中需要注意线程同步、锁、死锁等问题。

????????常见的多线程实现方式有以下几种:

1、继承Thread类方式

????????这种方式是定义一个类继承Thread类,然后重写run()方法,将要执行的任务写在run()方法中。创建线程实例后,调用start()方法启动线程。该方式实现简单,但如果类已经继承了其他类,则无法使用。

以下是一个继承Thread类的多线程案例:

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start();
        thread2.start();
    }
}

????????这个案例中,我们创建了一个继承了Thread类的MyThread类,并在它的run方法中打印了当前线程的名字和一个循环计数器。然后在主函数中,我们创建了两个MyThread对象并启动它们。当程序运行时,两个线程会交替打印出它们的计数器值,因为它们是同时运行的。

????????这个案例展示了如何继承Thread类来创建一个线程,并且启动线程的方法是调用start方法。每个线程都有自己的执行上下文,包含自己的栈空间和局部变量,因此两个线程之间的计数器值是相互独立的。

2、实现Runnable接口方式

????????Java实现Runnable接口方式的代码如下:

  1. 定义一个类实现Runnable接口,重写run()方法
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 编写线程执行的任务代码
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

  1. 创建MyRunnable实例并将其作为参数传入Thread类的构造方法中
public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

  1. 启动线程并执行任务

????????当调用Thread的start()方法时,JVM会自动调用MyRunnable实例的run()方法,从而执行线程的任务。

????????注意:在这种方式下,多个线程可以共享同一个MyRunnable实例,这意味着多个线程可以同时执行同一个任务。如果需要多个线程执行不同的任务,需要为每个线程创建一个单独的Runnable实例,并将其传入Thread的构造方法中。

????????这种方式是定义一个类实现Runnable接口,重写run()方法,将要执行的任务写在run()方法中。调用Thread类的构造函数,将该实现Runnable接口的对象作为参数传入,创建Thread实例后调用start()方法启动线程。该方式相比于继承Thread类方式,更加灵活,因为一个类可以实现多个接口。

3、实现Callable接口方式

????????Callable接口是Java提供的一个线程执行结果的返回值接口,需要通过Future接口获取执行结果。下面是Java实现Callable接口的示例代码:

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

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) throws Exception {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        Integer result = futureTask.get();
        System.out.println("计算结果:" + result);
    }
}

????????在上面的代码中,我们通过实现Callable接口,实现了call()方法,返回了一个Integer类型的结果。我们使用FutureTask类将Callable对象封装成一个异步任务,然后启动一个线程执行这个异步任务。最后,通过FutureTask的get()方法获取异步任务的执行结果。

????????注意:FutureTask的get()方法会阻塞当前线程,直到异步任务执行完毕并返回结果。如果异步任务执行时间过长,会导致当前线程一直阻塞。为了避免这种情况,我们可以使用FutureTask的get(long timeout, TimeUnit unit)方法,设置一个超时时间,超过这个时间后如果异步任务还没有执行完毕,则抛出TimeoutException异常,当前线程继续执行。

????????该方式类似于实现Runnable接口方式,但是需要实现Callable接口的call()方法,并返回一个结果。可以通过FutureTask类将Callable转换成Runnable来启动线程并获取返回值。

4、实现线程池方式

????????Java中线程池可以通过以下步骤实现:

? ? ? ? 1. 创建一个ExecutorService对象,它是Java中线程池的基本实现类之一,也是一个抽象类,可以使用Executors类中的静态方法来创建。

ExecutorService executorService = Executors.newFixedThreadPool(5);

????????这里创建了一个固定大小为5的线程池。

? ? ? ? 2. 在需要执行任务的地方,使用submit方法提交一个Runnable对象或Callable对象。

executorService.submit(new Runnable() {
    @Override
    public void run() {
        // 执行任务代码
    }
});

? ? ? ? 3. 执行完任务后,记得关闭线程池。

executorService.shutdown();

线程池的好处在于它可以避免重复创建和销毁线程,从而提高了程序的性能和效率。同时,线程池还可以控制并发线程的数量,防止系统崩溃。

????????通过使用线程池,可以更好地管理和复用线程,避免了线程的频繁创建和销毁。使用线程池是一种高效的多线程实现方式。

????????每种方式都有各自的特点,需要根据具体的实际情况选择合适的方式。

文章来源:https://blog.csdn.net/m0_37649480/article/details/134930087
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。