您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

JUC相关初步概念

Dongguo丶 发布时间:2021-09-14 10:50:06 ,浏览量:2

在并发编程中使用的工具类
java.util.concurrent   并发
java.util.concurrent.atomic   原子
java.util.concurrent.locks    lock锁
进程/线程相关知识 进程与线程 进程(Process)

进程(Process) 是计算机中的程序关于某数据集合的一次运行活动,它是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

线程(thread)

是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

通常一个进程可以包含若干个线程,每条线程并行执行不同的任务。

进程与线程二者对比

进程基本上相互独立的,而线程存在于进程内,是进程的一个子集

进程拥有共享的资源,如内存空间等,供其内部的线程共享

进程间通信较为复杂

​ 同一台计算机的进程通信称为 IPC(Inter-process communication 进程间通信)

​ 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP

线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量

线程更轻量,线程上下文切换成本一般上要比进程上下文切换低

总结:

进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程是资源分配的最小单位。

线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程是程序执行的最小单位。

并行与并发 串行

串行表示所有任务都一一按先后顺序进行。串行意味着必须先装完一车柴才能运送这车柴,只有运送到了,才能卸下这车柴,并且只有完成了这整个三个步骤,才能进行下一个步骤。

串行是一次只能取得一个任务,并执行这个任务。

并行

并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上则依赖于多核CPU。

image-20210910114539717

并发

并发(concurrent)指的是多个程序可以同时运行的现象,更细化的是多进程可以同时运行或者多指令可以同时运行。但这不是重点,在描述并发的时候也不会去扣这种字眼是否精确,

并发的重点在于它是一种现象, 并发描述的是多进程同时运行的现象。

但实际上,对于单核心CPU来说,同一时刻只能运行一个线程。所以,这里的"同时运行"表示的不是真的同一时刻有多个线程运行的现象,这是并行的概念,而是提供一种功能让用户看来多个程序同时运行起来了,但实际上这些程序中的进程不是一直霸占CPU的,而是执行一会停一会。由于CPU在线程间(时间片很短)的切换非常快,给我们的感觉是同时运行的。微观串行,宏观并行

要解决大并发问题,通常是将大任务分解成多个小任务, 由于操作系统对进程的调度是随机的,所以切分成多个小任务后,可能会从任一小任务处执行。这可能会出现一些现象:

• 可能出现一个小任务执行了多次,还没开始下个任务的情况。这时一般会采用队列或类似的数据结构来存放各个小任务的成果

• 可能出现还没准备好第一步就执行第二步的可能。这时,一般采用多路复用或异步的方式,比如只有准备好产生了事件通知才执行某个任务。

• 可以多进程/多线程的方式并行执行这些小任务。也可以单进程/单线程执行这些小任务,这时很可能要配合多路复用才能达到较高的效率

image-20210910114516721

总结:

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点

比如 你一边打电话一边吃饭

并行:多项工作一起执行。并行必须有多核才能实现,否则只能实现并发(伪并行)

比如 你吃一会饭,再去打一会电话,然后再继续吃饭,如果速度足够快,就给人一种吃饭打电话同时进行的感觉

image-20210910182433704

  1. 单核 cpu 下,多线程不能实际提高程序运行效率,只是为了能够在不同的任务之间切换,不同线程轮流使用cpu ,不至于一个线程总占用 cpu,别的线程没法干活

  2. 多核 cpu 可以并行跑多个线程,但能否提高程序运行效率还是要分情况的

    有些任务,经过精心设计,将任务拆分,并行执行,当然可以提高程序的运行效率。但不是所有计算任

    务都能拆分(参考【阿姆达尔定律】)

    也不是所有任务都需要拆分,任务的目的如果不同,谈拆分和效率没啥意义

  3. IO 操作不占用 cpu,只是我们一般拷贝文件使用的是【阻塞 IO】,这时相当于线程虽然不用 cpu,但需要一直等待 IO 结束,没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化

同步 异步 阻塞 非阻塞

同步:就是一个任务的完成需要依赖另外一个任务,只有等待被依赖的任务完成后,依赖任务才能完成。

如正在写的数据以后可能被另一个线程读到,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。

异步:不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,只要自己任务完成了就算完成了,被依赖的任务是否完成会进行通知。(异步的特点就是通知)。

打电话和发短信来比喻同步和异步操作。

当应用程序在对象上调用了一个需要花费很长时间来执行的方法,执行后的结果不会对接下来的操作有影响,就应该使用异步,在很多情况下采用异步途径往往更有效率。

事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。

阻塞:CPU停下来等一个慢的操作完成以后,才会接着完成其他的工作。

非阻塞:非阻塞就是在这个慢的执行时,CPU去做其他工作,等这个慢的完成后,CPU才会接着完成后续的操作。 非阻塞会造成线程切换增加,增加CPU的使用时间能不能补偿系统的切换成本需要考虑。

协程(纤程)

协程(Coroutines)是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。

img

协程不是被操作系统内核所管理的,而是完全由程序所控制,也就是在用户态执行。这样带来的好处是性能大幅度的提升,因为不会像线程切换那样消耗资源。

协程不是进程也不是线程,而是一个特殊的函数,这个函数可以在某个地方挂起,并且可以重新在挂起处外继续运行。所以说,协程与进程、线程相比并不是一个维度的概念。

既然协程这么好,它到底是怎么来使用的呢?

由于Java的原生语法中并没有实现协程(某些开源框架实现了协程,但是很少被使用),所以我们来看一看python当中对协程的实现案例,同样以生产者消费者模式为例:

img

代码中创建了一个叫做consumer的协程,并且在主线程中生产数据,协程中消费数据。

其中 yield 是python当中的语法。当协程执行到yield关键字时,会暂停在那一行,等到主线程调用send方法发送了数据,协程才会接到数据继续执行。

但是,yield让协程暂停,和线程的阻塞是有本质区别的。协程的暂停完全由程序控制,线程的阻塞状态是由操作系统内核来进行切换。

因此,协程的开销远远小于线程的开销。

管程

管程(monitor) 叫监视器,也就是所谓的锁

是保证了同一时刻只有一个线程在管程内活动,即管程内定义的操作在同一时刻只被一个线程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行

JVM中同步是基于进入和退出管程(monitor)对象实现的,每个对象都会有一个管程(monitor)对象,管程(monitor)会随着java对象一同创建和销毁

执行线程首先要持有管程对象,然后才能执行方法,当方法完成之后会释放管程,方法在执行时候会持有管程,其他线程无法再获取同一个管程

Object o = new Object();

new Thread(() -> {
    synchronized (o)
    {

    }

},"t1").start();

Monitor对象会和Java对象一同创建并销毁,它底层是由C++语言来实现的。

JVM第三版中:

image-20210904204945591

为什么多线程极其重要??? 硬件方面–摩尔定律失效

摩尔定律: 它是由英特尔创始人之一Gordon Moore(戈登·摩尔)提出来的。其内容为: 当价格不变时,集成电路上可容纳的元器件的数目约每隔18-24个月便会增加一倍,性能也将提升一倍。 换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。这一定律揭示了信息技术进步的速度。

可是从2003年开始CPU主频已经不再翻倍,而是采用多核而不是更快的主频。

摩尔定律失效。

在主频不再提高且核数在不断增加的情况下,要想让程序更快就要用到并行或并发编程。

软件方面

高并发系统,异步+回调等生产需求

为什么要使用多线程

执行一个简单的“Hello,World!”,却启动了那么多的“无关”线程,是不是把简单的问题复杂化了? 当然不是,因为正确使用多线程,总是能够给开发人员带来显著的好处,而使用多线程的原因主要有以下几点。 (1)更多的处理器核心 随着处理器上的核心数量越来越多,以及超线程技术的广泛运用,现在大多数计算机都比以往更加擅长并行计算,而处理器性能的提升方式,也从更高的主频向更多的核心发展。如何利用好处理器上的多个核心也成了现在的主要问题。 线程是大多数操作系统调度的基本单元,一个程序作为一个进程来运行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。试想一下,一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心加入也无法显著提升 该程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。 (2)更快的响应时间 有时我们会编写一些较为复杂的代码(这里的复杂不是说复杂的算法,而是复杂的业务逻辑),例如,一笔订单的创建,它包括插入订单数据、生成订单快照、发送邮件通知卖家和记录货品销售数量等。用户从单击“订购”按钮开始,就要等待这些操作全部完成才能看到订购成功的结果。但是这么多业务操作,如何能够让其更快地完成呢? 在上面的场景中,可以使用多线程技术,即将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列),如生成订单快照、发送邮件等。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短了响应时间,提升了用户体验。 (3)更好的编程模型 Java为多线程编程提供了良好、考究并且一致的编程模型,使开发人员能够更加专注于问题的解决,即为所遇到的问题建立合适的模型,而不是绞尽脑汁地考虑如何将其多线程化。一旦开发人员建立好了模型,稍做修改总是能够方便地映射到Java提供的多线程编程模型上。

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.1132s