Java 多线程实现1到1千万的求和操作

news2024/9/22 19:28:43

一、使用多线程的背景

  1. 提高程序速度和响应性:许多应用程序需要同时执行多个任务,例如网络服务器,图形图像处理,模拟程序等。使用多线程可以让程序同时执行多个部分,从而显著提高程序的执行速度、响应速度。

  2. 充分利用 CPU:在单核 CPU 的情况下,只有一个线程能够使用 CPU 进行计算。但随着 CPU 的多核化,可以同时开启多个线程,从而充分利用 CPU 资源。

  3. 方便协作编程:多线程编程可以让不同的开发者同时负责程序不同部分的开发工作,从而达成快速开发的目标。

  4. 提供更好的用户体验:对于复杂的应用程序,使用多线程可以让用户更好的体验程序的交互响应。

        总之,使用多线程能够将程序的执行与资源利用分离,使得代码更具可维护性、模块化,同时改善程序的执行效率和用户体验。

二、创建多线程的常见的几种方法(介绍两种常用的)

方式一、通过继承 Thread 类创建线程

public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
    //创建 Cat 对象,可以当做线程使用
    Cat cat = new Cat();
    /*
    (1)
    public synchronized void start() {
    start0();
    }
    (2)
    //start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现
    //真正实现多线程的效果, 是 start0(), 而不是 run
    private native void start0();
    */
    cat.start();//启动线程-> 最终会执行 cat 的 run 方法
    //cat.run();//run 方法就是一个普通的方法, 没有真正的启动一个线程,就会把 run 方法执行完毕,才向下执行
    //说明: 当 main 线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行
    //这时 主线程和子线程是交替执行.. System.out.println("主线程继续执行" + Thread.currentThread().getName());//名字 main
    for(int i = 0; i < 60; i++) {
        System.out.println("主线程 i=" + i);
        //让主线程休眠
        Thread.sleep(1000);
        }}
    }

    //1. 当一个类继承了 Thread 类, 该类就可以当做线程使用
    //2. 我们会重写 run 方法,写上自己的业务代码
    //3. run Thread 类 实现了 Runnable 接口的 run 方法
    /*
    @Override
    public void run() {
    if (target != null) {
    target.run();
    }
    }
    */
    class Cat extends Thread {
        int times = 0;
        @Override
        public void run() {//重写 run 方法,写上自己的业务逻辑
            //当 times 到 80, 退出 while, 这时线程也就退出.. }
            do {
                //该线程每隔 1 秒。在控制台输出 “喵喵, 我是小猫咪”
                System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());
                //让该线程休眠 1 秒 ctrl+alt+t
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (times != 80);
    }
}

 方式二、通过实现 Runnable 接口创建线程

        由于Java是单继承的,某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了。此时就可以通过实现Runnable接口来创建线程。

这里实现了用Runnable接口来创建线程并且在主线程中实现了两个子线程。

public class Thread01 {
    public static void main(String[] args) {
        //main 线程启动两个子线程
        Dog dog = new Dog();
        //dog.start(); 这里不能调用 start
        //创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread
        Thread thread = new Thread(dog);
        thread.start();//启动第 1 个线程
        Tiger tiger = new Tiger();//实现了 Runnable
        ThreadProxy threadProxy = new ThreadProxy(tiger);
        threadProxy.start();//启动第 2 个线程
    }
}

    class Animal {

    }

    class Tiger extends Animal implements Runnable {
        int count = 0;
        @Override
        public void run() { //普通方法
            do {
                System.out.println("老虎嗷嗷叫..hi" + (++count) +"线程名是:"+ Thread.currentThread().getName());
                //休眠 1 秒
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (count != 5);
        }
    }

    //线程代理类 , 模拟了一个极简的 Thread 类
    class ThreadProxy implements Runnable {//你可以把 Proxy 类当做 ThreadProxy
        private Runnable target = null;//属性,类型是 Runnable

        @Override
        public void run() {
            if (target != null) {
                target.run();//动态绑定(运行类型 Tiger)
            }
        }

        public ThreadProxy(Runnable target) {
            this.target = target;
        }

        public void start() {
            start0();//这个方法时真正实现多线程方法
        }

        public void start0() {
            run();
        }
    }

    class Dog extends Animal implements Runnable { //通过实现 Runnable 接口,开发线程
        int count = 0;

        @Override
        public void run() { //普通方法
            do {
                System.out.println("小狗汪汪叫..hi" + (++count) + "线程名是:"+ Thread.currentThread().getName());
                //休眠 1 秒
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while (count != 10);
        }
    }

 

Thread 和 Runnable 接口的区别:

 1. Thread 是一个类,Runnable 是接口,在 Java 语言里面的继承特性,接口可以支持多继承,而类只能单一继承。 所以如果在已经存在继承关系的类里面要实现线程的话,只能实现 Runnable 接口。

2. Runnable 表示一个线程的顶级接口,Thread 类其实是实现了 Runnable 这个接 口,我们在使用的时候都需要实现 run 方法。

3. 站在面向对象的思想来说,Runnable 相当于一个任务,而 Thread 才是真正处理的线程,所以我们只需要用 Runnable 去定义一个具体的任务,然后交给 Thread 去处理就可以了,这样达到了松耦合的设计目的。

4. 接口表示一种规范或者标准,而实现类表示对这个规范或者标准的实现,所以站在 线程的角度来说,Thread 才是真正意义上的线程实现。 Runnable 表示线程要执行的任务,因此在线程池里面,提交一个任务传递的类型是 Runnable。

总的来说,Thread 只是实现了 Runnable 接口并做了扩展,所以这两者并没什么可比性。

三、多线程实现1到1千万的求和操作

         该程序首先将1到1千万的数字平均分配给指定数量的线程进行求和,每个线程负责求和一部分数字。 每个线程求和完毕后,将结果发送到主线程中进行合并,最终得到总的求和结果。

public class SumMultiThread {
    private static final int MAX_NUM = 10000000;  // 求和的最大数值
    private static final int NUM_THREADS = 1000;  // 使用的线程数量

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        // 创建多个求和线程,并启动它们
        List<SumThread> threads = new ArrayList<>();
        for (int i = 0; i < NUM_THREADS; i++) {
            SumThread thread = new SumThread(i * (MAX_NUM / NUM_THREADS) + 1, (i + 1) * (MAX_NUM / NUM_THREADS));
            threads.add(thread);
            thread.start();
        }

        // 等待所有线程执行完毕
        for (SumThread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 合并所有线程的结果
        long sum = 0;
        for (SumThread thread : threads) {
            sum += thread.getSum();
        }

        long end = System.currentTimeMillis();
        System.out.println("Sum of 1 to " + MAX_NUM + " is " + sum + ", runtime is " + (end - start) + "ms");
    }

    static class SumThread extends Thread {
        private final int from;
        private final int to;
        private long sum;

        public SumThread(int from, int to) {
            this.from = from;
            this.to = to;
        }

        @Override
        public void run() {
            for (int i = from; i <= to; i++) {
                sum += i;
            }
        }

        public long getSum() {
            return sum;
        }
    }
}

四、使用多线程可能遇到的问题

        由于CPU、内存、I/O 设备速度有极大差异,为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系结构、操作系统、编译程序都做出了升级,但也随之出现了一些问题。主要体现为:

  • CPU 增加了缓存,以均衡与内存的速度差异;(导致 可见性问题)
  • 操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;(导致 原子性问题)
  • 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。( 导致 有序性问题)
  • 同时多线程的效率跟硬件有关,并不是开的线程越多越好。

以上一个 “1到一千万” 的求和为例,时间计算结果如下:

 

 

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/593988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CompletableFuture 线程编排

一、前言 Java8 新特性之一&#xff0c;其实现了Future<T>, CompletionStage<T>两接口&#xff0c;后者是对前者的一个扩展&#xff0c;增加了异步回调、流式处理、多个Future组合处理的能力&#xff0c;使 Java 在处理多任务的协同工作时更加顺畅便利。 二、Compl…

POST请求与GET请求的区别

POST请求 &#xff08;提交数据&#xff0c;一般用于将数据发给服务器&#xff0c;用于修改和写入数据&#xff09; 1、传参方式&#xff1a;相对安全&#xff0c;入参在request body中&#xff0c;可通过各种抓包工具获取 2、缓存&#xff1a;不会被缓存&#xff0c;保存在服…

基于Qt的嵌入式GUI开发指南(一)

Qt 是一个跨平台的应用程序开发框架&#xff0c;用于创建高性能、可扩展和用户友好的图形用户界面&#xff08;GUI&#xff09;应用程序。它提供了丰富的工具、库和功能&#xff0c;使开发者能够轻松地构建各种类型的应用程序&#xff0c;包括桌面应用程序、移动应用程序和嵌入…

Bellhop 从入门到上手

文章目录 前言一、Bellhop 简介二、Bellhop 结构1、输入文件2、输出文件 三、Bellhop 环境文件1、OPTIONS12、OPTIONS23、OPTIONS34、OPTIONS45、其他参数 四、BELLHOP&#xff08;Matlab_GUI&#xff09;实例1、bellhop 工具箱自取2、解压缩工具箱3、设置路径4、添加并包含子文…

高频面试八股文原理篇(六) mysql数据库的左连接,右连接,内链接有何区别

目录 内连接与外连接的区别 在sql中l外连接包括左连接&#xff08;left join &#xff09;和右连接&#xff08;right join&#xff09;&#xff0c;全外连接&#xff08;full join&#xff09;&#xff0c;内连接(inner join) 内连接与外连接的区别 自连接 一个表与它自身进…

Redis(一)常见命令使用

常见文件名Redis-cli使用命令1、启动Redis2、连接Redis3、停止Redis4、发送命令1、redis-cli带参数运行&#xff0c;如&#xff1a;2、redis-cli不带参数运行&#xff0c;如&#xff1a; 5、测试连通性 key操作命令获取所有键查询键是否存在删除键查询键类型移动键查询key的生命…

第二十章行为性模式—迭代器模式

文章目录 迭代器模式解决的问题结构实例存在的问题适用场景 JDK 源码 - Iterator 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。行为型模式分为…

“30分钟”带你走进线性回归的世界,轻松学习互联网营销推荐算法!

前言 本章主要介绍用于推荐系统的算法-线性回归算法的推导介绍&#xff0c;文章思路如下&#xff1a;由机器学习介绍&#xff0c;到监督学习&#xff0c;并重点介绍监督学习中回归问题里面的线性回归问题及推导。 可能需要大家具备一定的统计学、高数相关知识。 一、由机器学…

k8s入门(一)之pod创建、label使用、污点、亲和性、RS

一、创建一个pod [rootmaster01 ~]# kubectl create ns prod [rootmaster01 ~]# cat pod.yaml apiVersion: v1 kind: Pod metadata:name: pod-demonamespace: prodlabels:app: myapp spec:containers:- name: test1image: busybox:latestcommand:- "/bin/sh"- "…

问题记录 bug1-系统上电挂载异常分区,df与du命令查看文件使用大小显示不一样

linux磁盘分区 在Linux中&#xff0c;一切皆目录&#xff0c;每一块硬盘分区对应Linux的一个目录&#xff0c;所以我们可以通过管理目录来管理硬盘分区&#xff0c;而将硬盘分区与文件目录关联的操作就称为“挂载”【mount】&#xff0c;反之为“卸载”【unmount】 emmc&…

C高级 text

1.从命令行传参传入两个整数&#xff0c;整数1代表从整数几开始求和&#xff0c;整数2代表求和到整数几为止 2.打印99乘法表 3.输入年月日&#xff0c;计算是该年的第几天 1. 2. 3.

Linux进程基础

进程指正在运行的程序&#xff0c;如下图示&#xff0c;是资源分配的最小单位&#xff0c;可以通过“ps ”或“top”等命令查看正 在运行的进程&#xff0c;线程是系统的最小调度单位&#xff0c;一个进程可以拥有多个线程&#xff0c;同一进程里的线程可以共享此 进程的同一资…

Server版支持即将到期,Jira和Confluence如何迁移?(2)

到2024年2月&#xff0c;Atlassian将终止对Server产品及插件的所有支持。是时候制定您的迁移计划了——Atlassian为您提供两种迁移选择&#xff0c;一是本地部署的数据中心版本&#xff0c;中国用户25人以上即可使用&#xff0c;二是云版。作为Atlassian全球白金合作伙伴&#…

Markdown可以在线编辑吗?这个办法很好用

Markdown是一种轻量级标记语言&#xff0c;它使用简单的语法来创建文本&#xff0c;非常易于学习。它最初被设计为一种用于写作的格式&#xff0c;但现在已经成为了一种非常受欢迎的文本编辑工具。 作为一个较为方便的在线文本编辑器&#xff0c;它可以用代码代替文字&#xf…

一篇完整的测试方案怎么写

看上面的目录&#xff0c;详细 文档说明 文档名称 创建人/修改人 版本 时间 备注 v1.0 2022-11-17 新建 v1.1 2022-11-25 v1.2 2022-12-05 v2.0 2022-12-13 v2.1 2022-12-14 一、文档目的 为软件开发项目管理者、软件工程师、系统维护工程师、测试…

如何开发合成物品功能?

UE5 插件开发指南 前言0 数据结构1 合成面板UI组件2 小结前言 现在策划有一个合成物品的需求:可以将多个低级物品合成高级物品,如果背包中已有低级物品了,合成时需要减掉物品的费用,只需要支付合成费;提供玩家一个合成物品的层级视图,以便于玩家有节奏的购买物品,如下图…

电影《刀剑神域进击篇:暮色黄昏》观后感

上周看了电影《刀剑神域进击篇&#xff1a;暮色黄昏》&#xff0c;刀剑神域系列质量还是非常不错的&#xff0c; 本部电影讲述主角团队攻克boss&#xff0c;阻止公会团体互相打架的故事。 刀剑系列&#xff0c;记得当初是以一部连载动漫为开端&#xff0c;如果不是特别喜欢看动…

计算机网络—HTTP基本概念、HTTPS、HTTP状态码、HTTP缓存、HTTP请求

参考小林coding HTTP基本概念 HTTP是超文本传输协议。所谓的超文本&#xff0c;就是超越了普通文本的文本&#xff0c;最关键的是有超链接&#xff0c;能从一个超文本跳转到另一个超文本。 HTML是最常见的超文本&#xff0c;本身是纯文字文件&#xff0c;但是内部使用很多标签…

Scrum敏捷项目管理实例

这是一个Scrum敏捷单团队敏捷开发示例。 1、建立产品路线图 首先我们需要为这个项目创建一个产品路线图&#xff0c;产品路线图是一个高层次的战略计划&#xff0c;它描述了产品在未来一段时间可能会如何发展和壮大&#xff0c;产品路线图确保整个产品团队持续关注产品的目标…

GEE:对Landsat遥感影像进行处理,水体提取与可视化

作者:CSDN @ _养乐多_ 本文介绍了通过Google Earth Engine平台,并使用Landsat卫星遥感数据提取水体掩膜的方法和代码。通过裁剪和去除云等处理步骤,最终得到具有水体掩膜的影像,并进行可视化和导出。这种方法基于归一化水体指数(MNDWI)和OTSU阈值计算技术,使用了一个自…