多线程简介和在JAVA中应用

多线程简介和在JAVA中应用

什么是多线程what

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作"线程"(Thread),利用它编程的概念就叫作"多线程处理"。

处理器级别上实现"多线程"是采用一种并发执行机制,简单地说就是把一个处理器划分为若干个短的时间片或者,每个时间片依次轮流地执行处理各个应用程序,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果 (多个处理器就是多个处理器同时处理不同线程,真正意义上的多线程)。

把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序(进程)划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程,多个线程一起去协同完成任务,通过充分去共享资源来达到提升效率,这就是多线程程序。
线程里面的一些概念

进程,线程,串行,并发,并行,同步,异步,阻塞,非阻塞,死锁,信号量:
进程: 操作系统(OS)进行资源(CPU、内存、磁盘、IO、带宽等)分配的最小单位。

是OS对正在运行的程序的一种抽象,是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和 其它各种系统资源组成。打开一个浏览器、一个聊天窗口分别是一个进程。进程可以有多个子任务,如聊天工具接收消息、发送消息,这些子任务是线程。
线程: CPU调度和分配的基本单位。

一个或多个线程组成进程,每个线程都运行在同一进程的上下文中,共享进程资源(代码、全局变量等)。线程可比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。

每个进程至少有一个主执行线程,它无需由用户主动创建,一般由系统自动创建。系统创建好进程后,实际上就启动了执行该进程的执行主线程,执行主线程以函数地址形式,即程序入口函数(如 main函数),将程序的启动点提供给操作系统。主执行线程终止了,进程也就随之终止。
串行,并发,并行:
串行: 多个任务,执行时一个执行完再执行另一个。
并发: 多个线程在单个核心运行,同一时间一个线程运行,系统不停切换线程,看起来像同时运行,实际上是线程不停切换。
并行: 每个线程分配给独立的核心,线程同时运行。

单核CPU多个进程或多个线程内能实现并发(微观上的串行,宏观上的并行);多核CPU线程间可以实现微观上并行。
同步,异步,阻塞,非阻塞,信号量,死锁:

同步和异步描述的是消息通信的机制。
同步: 当一个request发送出去以后,会得到一个response,这整个过程就是一个同步调用的过程。哪怕response为空,或者response的返回特别快,但是针对这一次请求而言就是一个同步的调用。
异步: 当一个request发送出去以后,没有得到想要的response,而是通过后面的callback、状态或者通知的方式获得结果。
阻塞: 一般是被动的,在抢占资源中得不到资源,被动的挂起在内存,等待资源或信号将它唤醒。(释放CPU,不释放内存)
非阻塞: 指调用方发出请求的线程在没有等到结果时不会被挂起,直到得到结果后才返回,阻塞和非阻塞最大的区别就是看调用方线程是否会被挂起。
信号量: 其实就是一个变量,可以用一个信号量来表示系统中某种资源的数量,用户进程通过使用操作系统提供的一对原语来对信号量进行操作,从而方便的实现了进程互斥。
死锁: 在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。

为什么使用多线程why

从原理的角度考虑:

使用多线程主要目的是为了同步完成多项任务,主要目的不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。现在CPU基本都是多核的,如果只用单线程,那就是只用到了一个核心,其他的核心就相当于空闲在那里了,多CPU或多核CPU系统中,使用线程提高CPU利用率。
从用户的角度考虑:

为了得到更好的系统服务,并发、并行操作,提高应用程序响应速度(比如游戏界面的同时可以进行聊天;比如服务器端并发线程响应用户的请求)。
从程序的角度考虑:

就是合理地利用系统硬件资源来高效完成任务。

什么时候使用多线程when

1.高并发

比如系统接受实现多用户多请求的高并发时,通过多线程来实现。
2.后台任务

一个程序是线性执行的。如果程序执行到要花大量时间处理的任务时,那主程序就得等待其执行完才能继续执行下面的。那用户就不得不等待它执行完。这时候可以开线程把花大量时间处理的任务放在线程处理,这样线程在后台处理时,主程序也可以继续执行下去,用户就不需要等待。线程执行完后执行回调函数。
3.大任务

大任务处理起来比较耗时,这时候可以起到多个线程并行加快处理。

什么地方使用多线程where

比如: 从上千万条日志文件中统计出我们所需的信息,我们可以用一个线程专门负责日志文件的读取,另外一个线程则根据读取到的日志记录进行统计,最后得到汇总结果。这样的多线程程序相比单线程程序其执行效率可能快一倍。
再如: 手机上的音乐软件启动的时候可能会在你的手机的sd卡中搜索音乐文件(如mp3文件),然后将这些文件名自动添加到"本地播放列表"中。这里,搜索文件的操作如果放到负责图形化界面现实的线程(event loop线程)中做的话,那么这个软件启动的时候会"卡顿"。因此,这个操作通常会单独使用另外一个线程来执行。
再如: 我们项目中用到的Tomcat就是以多线程去响应请求的,可以在server.xml中配置maxPostSize等连接池、线程参数。Spring MVC响应请求原理也是这样,程序里数据库连接池也是结合多线程实现。

如何使用多线程how

1.熟悉开发语言中多线程使用方式:

多线程是一种技术,和开发语言无关,和操作系统平台相关,多数开发语言都有对多线程的支持。

比如JAVA开发中主要可以通过如下四种方式实现:

(1)继承Thread类

(2)实现Runnable接口

(3)实现Callable接口通过FutureTask包装器来创建Thread线程

(4)使用ExecutorService、Callable、Future实现有返回结果的多线程。
2.在程序需要的地方合理创建、使用、控制多线程:

比如线程执行时间控制、线程执行顺序控制、线程共享数据读写控制、线程数量控制(线程池)、线程执行异常控制。

JAVA多线程简介

JAVA是一种面向对象编程的跨平台开发语言,天生支持多线程,JAVA程序运行在JVM上,不同的系统平台有不同的虚拟机JVM,JAVA通过虚拟机访问系统资源(CPU等)实现多线程控制。
java线程相关概念:

线程的调度,线程的优先级,线程通信,线程的分类,线程的生命周期,线程的同步,线程安全,关键字、工具包和工具类、常用方法,实现方式。
线程的调度:

1.时间片调度:线程的调度采用时间片轮转的方式。

2.抢占式调度:高优先级的线程抢占CPU。
Java调度方式:

1.对于同优先级的线程组成先进先出队列(先到先服务),使用时间片策略。

2.对高优先级,使用优先调度的抢占式策略。
调度优先级:

等级:MAX_PRIORITY:10,MIN_PRIORITY:1,NORM_PRIORITY:5。
java线程通信:

通过Object类中的wait()/ notify()/ notifayAll()三个方法,结合线程的同步synchronize关键字使用,实现线程等待和唤醒操作。
线程分类:

1.守护线程(如垃圾回收线程,异常处理线程),2.用户线程(如主线程)。

线程的生命周期:

线程安全:

多个线程对同一个共享数据进行操作时,一个线程正准备操作共享数据,没来得及更新共享数据,另外线程同时也获取共享数据,没取到最新的数据,从而产生线程安全问题。比如开线程卖票,票的数量固定,每个线程都去操作相同变量(减少票数),部分线程获取不到最新的值会导致重复卖或多卖的情况。

线程安全通问题可以过同步机制解决,控制同一时刻只能有一个线程操作共享变量。java中使用同步代码块、锁等实现。

JAVA中多线程开发关键字、类、接口、工具包、常用方法、其他线程框架:
关键字: synchronized(非静态方法,静态方法,代码块),volatile(内存模型相关,1.一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。2.禁止进行指令重排序)。
接口、类: java.lang.Runnable,java.lang.Thread,java.lang.ThreadLocal,java.lang.ThreadGroup,ReentrantLock,Timer。
工具包: java.util.concurrent,java.util.concurrent.atomic。
Java多线程实现方法: 主要有四种方式:(1)继承Thread类;(2)实现Runnable接口;(3)实现Callable接口通过FutureTask包装器来创建Thread线程;(4)使用线程池工具类Executors、ExecutorService、Callable、Future实现多线程。

  1. 继承Thread类:
  2. 实现Runnable接口
  3. 实现Callable接口,通过FutureTask包装器来创建Thread线程,可接收返回结果
  4. 多线程实现方式4(线程池):使用Executors、ExecutorService、Callable、Future实现有返回结果的线程。


    上述代码中Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService
    接口,工厂方法说明如下:

    同时ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,调用返回Future对象的get()方法,会阻塞直到计算完成。
    如果不需要等待返回值,可以使用其中的execute方法传入实现了runnable接口的对象。
  5. 其他线程任务调度框架:Quartz和spring scheduling,xxl-job等
    Quartz是一个功能丰富的开源的任务调用系统,它可以创建简单或者复杂的几十、几百、甚至成千上万的job。
    此外,quartz调度器还支持JTA事务和集群,主要功能:

    1.持久性作业 - 就是保持调度定时的状态。
    2.作业管理 - 对调度作业进行有效的管理。
    SpringBoot自带的 Schedule,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单,主要功能:
    1.可以按周期进行执行,如每天3点执行一次,符合cron表达式。
    2.可以按fixedDelay延迟执行,每隔几秒执行一次。
    3.可以按fixedRate执行,没有延迟,直接每隔几秒执行一次。
    注意:scheduling默认是一个单线程处理的程序。
    SpringBoot中的核心具有集成了Scheduled功能,使用步骤如下:
    1.在@SpringBootApplication中基础上写@EnableScheduling 开启定时任务。
    2.在具体的组件的方法需要使用注解: @scheduled(cron="") 或@scheduled(fixedDelay=2000)或@scheduled(fixedRate=2000)或@Scheduled(fixedRateString = "2000")或@Scheduled(fixedRateString= "2000") @Scheduled(fixedRateString = "${schedule.sesonds}")。

多线程在项目中的应用

A项目,B平台,C系统,D系统等项目系统中,都有使用多线程,下面介绍项目中主要使用多线程的地方。

A项目

  1. 多数据源中使用多线程进行连接缓存控制:synchronized。
  2. 多数据源中使用多线程进行动态数据源连接切换控制:ThreadLocal

  3. 多线程定时器触发:Timer

  4. 使用quartz框架实现预警报警定时监测任务:org.quartz.Scheduler

  5. 使用scheduling实现数据同步:SchedulingConfigurer,Executors.newScheduledThreadPool(int corePoolSize)

B平台

使用Executors工具类创建线程池结合CountDownLatch执行"多个实时数据预警报警模型",利用系统资源,减少执行时间:

C系统-文件采集

采集程序说明:
项目说明: 传txt文件到ftp服务器,然后ftp服务器上部署解析程序解析校验文件,文件采集程序分为读取、校验转换(异常校验,转JSON)、推送三个步骤,其中读取和解析两个步骤需要操作磁盘IO和数据判断(CPU运算),执行比较慢,使用单线程的情况下,如果文件多了或者矿上补传历史数据,很容易导致文件积压,数据接入实时性得不到保证,影响用户使用。

前期先后开发了window版本解析程序(C#开发)、linux版本(python开发 )、linux版本(java开发),由于C系统特殊性,只提供了一台windows版本服务器,接入数据格式、实际可用环境和和之前的不完全一样,接入初期使用的java开发的linux版,在window服务器上安装了linux虚拟机部署ftp和采集进行文件接入采集,考虑后面维护便捷、系统稳定、后期其他类似项目的可复用性,适应环境实际需要的同时提高文件采集速度、解决跨平台问题,对文件采集程序进行了改造,去掉了系统平台相关的东西,使采集程序既支持window系统又支持linux系统,并开发了多线程读取、解析校验、推送功能。

下面结合代码介绍多线程在项目中的应用:

  1. 文件处理多线程基础类:FileTask,ScheduledThreadPoolExecutor
  2. 文件监听:NioFileWatchTask,volatile
  3. 文件解析:FileParseTask
  4. 文件推送:FileSendTask

D系统

多线程在D系统中使用背景说明:

该系统其中一项主要功能是对几千个公司数据进行分析,分析计算结果并做各种统计展示。

其中分析方式分为多种,一种又对应多个子项任务,目前已整理的子任务就有几十到一百项,每个公司每个子项对应不同数据,有的是需要实时获取的有的是固定的,我们需要自定义频率定时跑批作业按分析方式和子任务对公司数据进行动态分析,系统后台按子任务进行分析计算是多个大任务,对服务器系统资源、程序效率要求都比较高。

综合考虑服务器资源、程序高效和稳定性,我们单独抽取了定时任务模块(和系统WEB服务分开,不影响系统服务),在里面引入了Spring+Quartz框架并结合Spring scheduling,实现了集群、分布式多线程定时任务,使得不同任务可以在不同服务器上执行,充分利用服务器硬件资源,减轻单台机器的负担,也在每个定时任务里适当使用了多线程控制,比如执行几十上百个子任务时,使用了固定线程池,又比如使用Spring scheduling配置了定时更新计算任务线程,利用CPU资源提升评估效率。

D系统定时任务、多线程应用图解:

多线程使用总结

从上面介绍可知,多线程和平台、操作系统设备相关,我们常说的多线程开发是指使用java,c#,python等高级开发语言,结合提供的工具包、类、函数方法关键字等编写多线程程序实现对所运行平台系统硬件资源进行多线程访问使用,来高效完成目标任务。多线程程序可以很简单也可以很复杂,不能为了使用而使用,而是需要使用的时候才使用。

当我们准备使用或使用多线程编程有哪些需要注意事项?
1. 确定程序运行的操作系统硬件环境和软件环境是否适合运行多线程程序,比如软件运行环境机器cpu占用高或者磁盘占用高,使用多线程可能会加大cpu负荷或磁盘负荷,并且执行效率还不如单线程程序,还会对其他程序运行产生大的影响。如何必须使用就需要考虑增加服务器资源配置或者使用其他负载不大的服务器。
2. 操作系统软、硬件环境占用都不高,我们需要开发使用多线程程序时,也不能盲目使用多线程,比如一次开很多个线程,远远超过系统支持的并发处理数,同样会影响系统性能和影响同服务器的其他应用程序。线程开多少合适呢?这个就需要进行性能和负载测试,个人建议:就少不就多,能一个线程解决的问题不开两个线程处理,因为线程调度会有系统开销,有时候线程任务开的越多程序复杂度也会跟着增加。
3. 了解多线程原理和开发语言中的常用方式,大多数程序软件都不是一蹴而就的,不断改进、优化自己的程序。

相关推荐
余辉zmh3 小时前
【C++篇】:ServiceBus RPC 分布式服务总线框架项目
开发语言·c++·rpc
rechol3 小时前
类与对象(中)笔记整理
java·javascript·笔记
Tony Bai3 小时前
释放 Go 的极限潜能:CPU 缓存友好的数据结构设计指南
开发语言·后端·缓存·golang
周杰伦_Jay3 小时前
【Spring Boot从入门到精通】原理、实战与最佳实践
java·spring boot·后端
呼哧呼哧.3 小时前
SpringBoot 的入门开发
java·spring boot·后端
观望过往4 小时前
【Java数据结构】队列详解与经典 OJ 题目实战
java·数据结构
仲夏幻境4 小时前
js利用ajax同步调用如何
开发语言·javascript·ajax
天地人-神君4 小时前
将.idea取消git托管
java·git·intellij-idea
譕痕4 小时前
Idea 启动报 未找到有效的 Maven 安装问题
java·maven·intellij-idea