成都汇智动力老师直接接听

400-029-09** 400-029-0997 转 65635
查看完整号码
扫码拨号
微信扫码拨号

Java开发之高并发必备篇(一)——线程基础

作者:汇智动力学院 来源:汇智动力学院 2022/10/6 14:23:13

提到高并发,这几年几乎是火遍编程界的网络名词了。无它,随着现...

提到高并发,这几年几乎是火遍编程界的网络名词了。无它,随着现在互联网的高速发展特别是电商平台类的应用快速发展,互联网服务内容也越来越丰富,用户越来越多,淘宝、天猫、京东、“拼夕夕”、抖音等几乎成为了广大群众每日必用的应用了。而在这些应用中见到的“天猫双11”、“京东618”、“商品秒杀”、“火车票抢票”往往都是短时间内产生大量的并发访问量和流量,如果不解决我们也不是没有见过天猫双11崩溃的时候!那么如何解决这种高并发问题的首要基础就是能够玩好线程,所以下面的内容我们就来学习下线程。 1.为什么要有线程? 举个简单例子,假设我们在使用百度网盘这个应用,我们想要使用百度网盘的上传和下载功能。没有线程的话那么我们的操作只能是这样的:我们上传文件的时候就不能干别的事,需要我们上传成功之后才能下载别的东西,并且上传文件也只能一个个的上传,那将是很糟糕的一个体验。那么我们想要百度网盘即可以一边上传一边下载,并且可以多个上传多个下载这样的多任务操作应该怎么办呢?没错就是使用线程了! 现在的操作系统不管是windows也好、linux系列的也好基本上都是多用户多任务的操作系统,而多任务就是靠多线程来实现的。多任务执行也就是所谓的并发。 2.操作系统中进程和线程的概念 提到线程,我们就不得不先提下进程,往往很多人认为一个进程就是一个程序,那么是不是这么一回事呢?我们来看看进程的定义。 进程概述 系统中能够独立运行的程序被称为一个进程。进程是CPU分配资源的最小单位; 例如:windows的进程
每个进程都有自己独立的一块内存空间,一个单核CPU是单进程处理,即同一时间只能处理一个进程,但是系统可以分配给每个进程一段有限的执行 CPU 的时间(被称为 CPU 时间片),CPU 在这段时间中执行某个进程,然后下一个时间段可能又跳到另一个进程中去执行,因为CPU切换的速度太快了,远远超出了我们肉眼的识别能力,所以我们看到很多的进程似乎都是同时在运行一样。而多核CPU则可以实现同时多个进程的执行,只不过因为调度的问题可能导致一个核心可能一个时间片内调用多个进程。 线程的概述 线程是进程中完成一个流程的执行任务。是进程中的一个执行路径,跟进程共享一个内存空间。线程是程序中运行的最小单位。 线程本身不能单独存在,需要运行在进程中。一个进程可以有多个线程(例如:一个Java程序基本上都有main主线程和GC线程(垃圾回收器)等两个以上线程),同一进程的所有线程共享本进程的资源。 3.Java中的线程实现 通过前面的介绍我们认识了线程,那么在 Java中线程是如何实现的呢?在Java中线程的主要实现方式有以下三种: 继承Thread类 实现Runnable接口 实现Callable接口 前两种实现方式是比较常见的方式并且类和接口也都在java.long包下,第三种Callable在java.util.concurrent包中我们在后面部分会介绍其实现以及不同,我们先来看下前面两种的实现方式。 (1)继承Thread类 首先我们看到其实Thread类也是实现了Runnable接口
这样我们直接创建Thread对象就可以使用了。 使用步骤: A.继承Thread类或者直接创建Thread对象 B.在run方法中实现线程任务代码 C.调用Thread的start方法启动线程
分析: 下面的方式使用起来不需要定义类使用简单一些,适合调用次数较少的情况。另外虽然Thread类线程启动之后执行的是run方法,但是线程的启动方法是start方法! start方法和run方法的区别: run方法只是单纯的Thread类的一个普通方法,只不过线程启动的时候会调用而已,如果只是使用 thread.run()那么也仅仅代表我们调用了thread的run方法但是并没有开启一个新的线程,代码还是在主线程main中执行。 start方法调用表示创建了一个新的线程,线程进入准备就绪状态,当线程得到cpu时间片处理时,Java 虚拟机就会调用该线程的 run 方法并执行里面的任务代码,任务代码执行完毕则线程结束。另外start开启的新的线程是一个独立的执行任务,下面的代码无须等待线程执行完毕就可以继续执行。 使用Thread类的局限性: Thread类实现方式虽然简单,但是因为Thread是一个类,在Java中类只能进行单继承,所以对于线程的扩展能力就差。 (2)实现Runable接口
之前我们也介绍了Thread类其实也是实现了Runable接口,其中启动线程执行任务代码的run方法其实就是Runnable接口的方法。所以我们可以通过实现Runable接口配合Thread类实现线程的扩展使用。 使用步骤: A.定义一个类实现Runnable接口 B.在run方法中实现线程任务代码 C.通过Thread(Runnable r)构造方法传入Runnable接口实例对象,并调用start方法启动线程
分析: 使用Runnable接口的实现,因为接口可以多实现的特点,所以Runnable接口可以被更多的类实现,扩展性比Thread要强,另外又因Runnable是作为Thread构造方法传入才创建的线程所以Runnable需要依赖与Thread类使用,并且多个Thread对象可以使用同一个Runnable实例。 Runnable的Lambda表达式的使用: Java8提供了Lambda表达式,我们使用Lambda表达式创建Runable实现类实例的时候不需要我们定义类,变得更加方便。代码实现如下:
4.多线程经典案例-卖票案例 虽然开发中通过继承Thread类和实现Runnable接口都可以实现线程,但是因为继承Thread类具有局限性。而实现Runnable接口更容易扩展,并且实现Rnnnable接口的实例可以被多个Thread对象共享,这样解决一些多线程处理资源就更方便一些。我们以卖票的案例来说明: 我们有三个窗口在卖票,总共有100张票,那我们怎么实现3个窗口同时都在卖,并且卖掉100张票呢? (1)Thread类的实现
运行的结果:
结论: 最后发现每一个窗口都是卖100张票,总共卖了300张票,但是一共只有100张票,显然继承Thread类实现不方便,因为t1、t2、t3三个窗口线程不能共享100张票这个资源,所以导致都各自卖了100张。 Runnable接口的实现
运行的结果:

结论: 我们发现使用Runnable接口的方式可以让t1、t2、t3三个窗口线程同时在卖100张票,而不是每个线程都卖100张,这是因为t1、t2、t3都使用了同一个Runnable实例。显然Runnable接口的实现可以实现100张票资源的共享,但是通过运行结果我们会发现一个令人困惑的问题,就是不管程序运行多少次,总是有两个或者三个线程卖了同一张票!这是什么原因导致的呢?其实这就是所谓的著名的 “多线程并发访问不安全的问 题”! 后面的篇章中我们会介绍为什么多线程访问对象会不安全。 二、算法的设计原则   ①、正确性:首先,算法应当满足以特定的“规则说明”方式给出的需求。其次,对算法是否“正确”的理解可以有以下四个层次:   一、程序语法错误。   二、程序对于几组输入数据能够得出满足需要的结果。   三、程序对于精心选择的、典型、苛刻切带有刁难性的几组输入数据能够得出满足要求的结果。   四、程序对于一切合法的输入数据都能得到满足要求的结果。   PS:通常以第 三 层意义的正确性作为衡量一个算法是否合格的标准。   ②、可读性:算法为了人的阅读与交流,其次才是计算机执行。因此算法应该易于人的理解;另一方面,晦涩难懂的程序易于隐藏较多的错误而难以调试。   ③、健壮性:当输入的数据非法时,算法应当恰当的做出反应或进行相应处理,而不是产生莫名其妙的输出结果。并且,处理出错的方法不应是中断程序执行,而是应当返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。   ④、高效率与低存储量需求:通常算法效率值得是算法执行时间;存储量是指算法执行过程中所需要的*大存储空间,两者都与问题的规模有关。   前面三点 正确性,可读性和健壮性相信都好理解。对于第四点算法的执行效率和存储量,我们知道比较算法的时候,可能会说“A算法比B算法快两倍”之类的话,但实际上这种说法没有任何意义。因为当数据项个数发生变化时,A算法和B算法的效率比例也会发生变化,比如数据项增加了50%,可能A算法比B算法快三倍,但是如果数据项减少了50%,可能A算法和B算法速度一样。所以描述算法的速度必须要和数据项的个数联系起来。也就是“大O”表示法,它是一种算法复杂度的相对表示方式,这里我简单介绍一下,后面会根据具体的算法来描述。   相对(relative):你只能比较相同的事物。你不能把一个做算数乘法的算法和排序整数列表的算法进行比较。但是,比较2个算法所做的算术操作(一个做乘法,一个做加法)将会告诉你一些有意义的东西;   表示(representation):大O(用它*简单的形式)把算法间的比较简化为了一个单一变量。这个变量的选择基于观察或假设。例如,排序算法之间的对比通常是基于比较操作(比较2个结点来决定这2个结点的相对顺序)。这里面就假设了比较操作的计算开销很大。但是,如果比较操作的计算开销不大,而交换操作的计算开销很大,又会怎么样呢?这就改变了先前的比较方式;   然后我们再说说算法的存储量,包括:    程序本身所占空间;    输入数据所占空间;    辅助变量所占空间;   一个算法的效率越高越好,而存储量是越低越好。 三、算法的分类 算法可以宏泛的分为三类: 一,有限的,确定性算法 这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。 二,有限的,非确定算法 这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是的或确定的。 三,无限的算法 是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。 Java中常见的算法有: ①、排序 排序就是对一组数据按照一定的顺序(从大到小或者从小到大)进行排序; 常见排序如下: 简单排序:冒泡排序、选择排序、插入排序; 高级排序:快速排序、希尔排序、归并排序、基数排序、鸡尾酒排序等等; ②、递归 递归是一种直接或者间接调用自身的一种算法,递归的目的是简化程序设计使程序更加易读; ③、查找 在一些(有序的/无序的)数据元素中,通过一定的方法找出与给定关键字相同的数据元素就叫做查找; ④、统计 指对有关数据的搜集、整理、计算、分析、解释、表述等的活动。 往期文章 墙裂推荐 [ 1] Java开发篇——设计模式(5)装饰设计模式 [2] Java开发篇——设计模式(4)先生需要代购吗? [3] Java开发篇——设计模式(3)面试被问工厂模式?不要怕看这里! 原创视频 墙裂推荐 2022转行软件测试之开班动态 ↓↓↓

活动福利 // 1 毕业礼包 | 毕业学员免费赠送《软件测试技术大咖专题课》,助力学员早日突破高薪瓶颈 // 2 入职礼包 |就业学员免费赠送《Java语言开发视频课》及全套源代码,市场价值12800元 // 3 推荐有奖 |推荐好友成功报名,立得丰厚“伯乐”红包 (欢迎详询校区老师)

详询软件测试&开发培训事宜

添加微信咨询
杨老师 @成都汇智动力

专业解答各类课程问题、介绍师资和学校情况

微信号:186******73

立即咨询

“成都汇智动力”是成都汇智动力信息技术有限公司在教育宝平台开设的店铺,若该店铺内信息涉嫌虚假或违法,请点击这里向教育宝反馈,我们将及时进行处理。

机构评分

环境:4.5师资:4.5服务:4.0效果:4.0

公示信息

店铺名称:成都汇智动力

单位名称:成都汇智动力信息技术有限公司

账号名称:cdhzdl(180******07)

所属城市:四川成都

入驻时长:11年

在线客服:在线聊

微信咨询

返回顶部