Java多线程的六种使用方式

 一:继承 Thread 类

  • 描述: 通过继承 Thread 类并重写 run() 方法来定义任务。创建 Thread 对象后,调用 start() 方法启动线程。
  • 优点: 简单直接,适合执行简单的任务。
  • 缺点: 只能继承 Thread 类,不能继承其他类,扩展性差。
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

二:实现 Runnable 接口

  • 描述: 实现 Runnable 接口并重写 run() 方法。通过将 Runnable 对象传入 Thread 构造函数来启动线程。该方式允许一个类实现多个接口,避免了继承的局限性。
  • 优点: 灵活性高,可以实现多个接口。
  • 缺点: 需要显式地将 Runnable 传递给 Thread,稍微多了一些步骤。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable thread is running");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

三:使用 Executor 框架

  • 描述: Executor 框架是 Java 5 引入的,用于管理线程池。它通过 ExecutorService 接口提供了更高级的多线程管理方式,支持任务提交、结果获取等功能。通过 ExecutorService 可以避免手动创建和管理线程,提供更好的线程池管理。
  • 优点: 线程池可以复用线程,减少线程创建的开销;支持任务调度和批量任务执行;易于扩展和管理。
  • 缺点: 相比直接使用 Thread 或 Runnable,需要学习和理解线程池的相关知识。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        executor.submit(() -> System.out.println("Task is running"));
        executor.shutdown();
    }
}

  • 常见的 ExecutorService 实现:
    • newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
    • newCachedThreadPool():创建一个可缓存的线程池,线程池的大小根据需求自动调整。
    • newSingleThreadExecutor():创建一个单线程化的线程池。
    • newScheduledThreadPool(int corePoolSize):创建一个支持定时任务调度的线程池。

四: 使用 ForkJoinPool(适用于分治任务)

  • 描述: ForkJoinPool 是 ExecutorService 的一种扩展,主要用于执行可以递归拆分的任务,适合执行分治类型的任务。例如,处理大数据集时,可以将任务分解成多个小任务并并行执行,然后将结果合并。
  • 优点: 支持大规模任务的分治操作,自动管理线程的划分和合并。
  • 缺点: 适用于特定类型的任务,不适合所有的多线程场景。
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

public class Main {
    static class SumTask extends RecursiveTask<Integer> {
        private final int[] array;
        private final int start, end;

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

        @Override
        protected Integer compute() {
            if (end - start <= 10) {
                int sum = 0;
                for (int i = start; i < end; i++) {
                    sum += array[i];
                }
                return sum;
            } else {
                int middle = (start + end) / 2;
                SumTask leftTask = new SumTask(array, start, middle);
                SumTask rightTask = new SumTask(array, middle, end);
                leftTask.fork();
                rightTask.fork();
                return leftTask.join() + rightTask.join();
            }
        }
    }

    public static void main(String[] args) {
        int[] array = new int[100];
        for (int i = 0; i < array.length; i++) {
            array[i] = i + 1;
        }

        ForkJoinPool pool = new ForkJoinPool();
        SumTask task = new SumTask(array, 0, array.length);
        int result = pool.invoke(task);
        System.out.println("Sum is: " + result);
    }
}

五: 使用 CompletableFuture(支持异步编程)

  • 描述: CompletableFuture 是 Java 8 引入的一个异步计算类,它支持非阻塞异步编程,能够更加灵活地组合多个异步任务。它可以与 ExecutorService 配合使用,允许你创建异步任务并在其完成时处理结果。
  • 优点: 支持链式调用,能够灵活地处理任务的组合和异常。
  • 缺点: 对于简单的多线程任务,CompletableFuture 的使用可能会显得过于复杂。
import java.util.concurrent.CompletableFuture;

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

        future.thenRun(() -> System.out.println("After task completion"));
        future.join();  // 等待任务完成
    }
}

六:使用 Parallel Streams(并行流)

  • 描述: Java 8 引入了流(Stream)API,可以使用 parallel() 方法将流操作转化为并行执行。这是一种高层次的并行处理方式,适合处理大量数据的并行计算。
  • 优点: 使用起来非常简单,适合在不需要显式控制线程的情况下进行并行计算。
  • 缺点: 并行流的性能提升取决于操作的复杂性和数据量,并且对小规模任务的提升效果有限。

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
        System.out.println("Sum is: " + sum);
    }
}

总结:

  • 继承 Thread 类:简单直接,但不灵活。
  • 实现 Runnable 接口:适合需要实现多个接口的场景。
  • Executor 框架:推荐的线程池管理方式,适合中大型项目。
  • ForkJoinPool:适用于分治任务和大规模计算任务。
  • CompletableFuture:支持异步编程和链式任务处理。
  • 并行流(Parallel Streams):适合大规模数据的并行处理,易于使用。
1. 建立三个线程,并且同时运行它们。当运行时输出线程的名称。 实验步骤: (1)、创建类sy6_1 (2)、创建三个线程,调用start()方法启动这三个线程 (3)、保存文件,调试并编译运行程序。 参考程序运行效果: 2. 实现3个类:Storage、Counter和Printer。 Storage类应存储整数。 Counter应创建线程,线程从0开始计数(0,1,2,3…)并将每个值存储到Storage类中。 Printer类应创建一个线程,线程读取Storage类中的值并打印值。编写程序创建Storage类的实例,并创建一个Counter对象和Printer对象操作此实例。 实验步骤: (1)、创建三个类Counter, Printer,Storage (2)、创建TestCounter类,在该类中定义main函数,在main函数中定义Storage对象、Counter对象和 Printer对象,创建Counter线程和Printer线程并启动 (3)、保存文件,调试并编译运行程序。 参考程序运行效果: 3. 修改实验1第2题的程序,添加适当代码,以确保每个数字都恰好只被打印一次。 实验步骤: (1)、创建三个类Counter, Printer,Storage (2)、 创建TestCounter类,在该类中定义main函数,在main函数中定义Storage对象、Counter1对象和 Printer对象,创建Counter线程和Printer线程并启动 (3)、在定义Storage类中的setValue(int i) 和getValue ()方法时使用synchronized关键字,将其定义为同步方法 (4)、保存文件,调试并编译运行程序。 参考程序运行效果:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值