Java 多线程加法求和

Java 多线程加法求和

代码

先上代码再上解析:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class Sum implements Runnable {
    private final int[] numbers;
    private final int start;
    private final int end;
    private final AtomicInteger total;

    public Sum(int[] numbers, int start, int end, AtomicInteger total) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
        this.total = total;
    }

    @Override
    public void run() {
        int localSum = 0;
        for (int i = start; i < end; i++) {
            localSum += numbers[i];
        }
        // addAndGet 可以原子地(即线程安全地)将给定的数值增加到当前数值
        System.out.printf("local %d\n", localSum);
        total.addAndGet(localSum);
    }
}

class MultiThreadedAddition {
    public static void main(String[] args) throws InterruptedException {
        // 实现从1加到100
        int[] numbers = new int[101];
        for (int i = 1; i < numbers.length; i++) {
            numbers[i] = i;
        }

        AtomicInteger total = new AtomicInteger();
        // 假设有4个线程并发计算
        int threadCount = 4;

        // Executors.newFixedThreadPool 用于创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

        // 把 numbers.length 个(100个)任务均分给 threadCount 个(4个)线程
        int partitionSize = numbers.length / threadCount;
        for (int i = 0; i < threadCount; i++) {
            int startIndex = i * partitionSize;
            // 这里要特判一下最后一个线程的结束范围,因为可能不能恰好均分任务,比如999个任务给4个线程做...
            int endIndex = (i == threadCount - 1) ? numbers.length : (i + 1) * partitionSize;
            Sum task = new Sum(numbers, startIndex, endIndex, total);
            executorService.submit(task);
        }

        // 先关闭线程池
        executorService.shutdown();
        // 阻塞主线程,让其等待所有线程提交结果(或者直到超过最大等待时间,不过这里设置的是MAX_VALUE,就是无限等待
        executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

        System.out.println(total.get());
    }
}

运行结果

这里是做了一个1~100的求和,我们知道结果是5050,运行检验一下:
Java 多线程 加法求和1到100

多线程的要点解析

多线程创建:Runnable Callable Thread

Runnable 接口
  1. 基本概念
    Runnable 是一个非常基础的接口,只有一个方法 void run()。任何实现了 Runnable 接口的类都可以作为一个线程的工作单元(任务)。
  2. 传递给 Thread 类的构造函数
    当一个 Runnable 实例被传给 Thread 构造函数时,可以创建一个新的线程,然后调用 Thread.start() 方法启动该线程来执行 run() 方法中的代码。
  3. 没有返回值
    Runnable 不支持返回值,并且在 run() 方法内部抛出的异常不会被捕获并转发给调用者。
Thread 类
  1. 基本概念
    Thread 是Java中用于实现线程的基础类,它实现了 Runnable 接口,所以可以通过继承 Thread 类并重写 run() 方法来创建一个可运行的线程。
  2. 接收 Runnable 实例

呼应上文

通过创建 Thread 类的子类或通过传递一个 Runnable 对象给 Thread 构造函数,可以创建和管理线程。
3. 更多 API
Thread 类提供了更多控制线程生命周期的方法,如 start()、join()、interrupt()、setName() 等。

Callable 接口
  1. 基本概念
    Callable 是在Java 1.5以后引入的,相比于 Runnable,它更加灵活。

  2. 能返回值和抛出异常
    可以通过 call() 方法返回一个结果,而且 call() 方法可以抛出受检异常(checked exception)。

3. 使用
Callable 实例不能直接启动,需要通过 ExecutorService 提交给线程池执行,并通过 Future 对象获取异步计算的结果。
使用 Callable 通常结合 FutureTask(实现了 Runnable 和 Future 接口)来包装 Callable 任务,并将其提交给线程池执行,从而可以获得线程执行的结果。

结束线程的工作

上面的代码中结束线程工作的过程中,涉及到了两个API

  • executorService.shutdown()
    相当于关闭线程池,不允许再扔新的线程或者任务进来了。
  • executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)
    如果一个线程调用这个方法,那么它就会被阻塞,暂时停止活动直到这个线程池里面的所有线程结束工作,或者等待超过指定时间——这里我们设置了 Long.MAX_VALUETimeUnit.NANOSECONDS,前者是指 无限期等待,后者是指时间单位为 纳秒

这里我们是主线程调用的,所以就是主线程被阻塞.

避免竞态

上述代码中的多线程加法求和用到了 AtomicInteger,它是 java.util.concurrent.atomic 包下原子整数类,顾名思义,它所对应整数上发生的操作都是原子性的,线程只能串行的去累加它,就能避免竞态问题。

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

碳苯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值