前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java 并发篇- 概念篇

java 并发篇- 概念篇

作者头像
haoming1100
发布2019-07-30 15:54:14
4370
发布2019-07-30 15:54:14
举报
文章被收录于专栏:步履前行步履前行

各位好,今天是我们并发篇正式开始的第一篇,既然我们大家学习并发,那么就要理解一些计算机概念最好,否则,知道怎么用而不知道名称是啥,概念含糊不清,以及不知道怎么设计的,假如今天你突然换 go 语言,设计个并发还是不会。我们要学的是并发思想,在Java 中的思想,一通则百通,而不是背代码,切记切记。


让我们用面向对象的方法,把我们日常中的房子做为对象来解释线程和进程的工作。

房子首先是一个装东西的容器。它本身有自己的属性 -- 面积,卫生间数量,卧室数量,客厅,书房等等。

房子是被人住的,所以它是一个被动方。

作为房子的使用者 - 人,就是操作方,使用电视,做饭,洗澡,撒狗粮。都属于行为了。

单线程

如果是一个人在家里,那么你可以想干啥就干啥,毕竟我得地盘我做主。

多线程

假如你结婚了,那么就不能随自己心愿了,比如,你的另一半在厕所,那么你就不能进去了,因为毕竟被别人占据着。 当然,因为你们是2个人,所以理论上家里会相对于一个人更加安全,比如煤气泄漏,房门上锁等(当然你一个人也可以),这个称为理论上安全(另一个要和你一起去GG,这不就不安全了呗)。

但是,结婚多年的你们擦枪走火,造出了熊孩子,这时候各种破坏,你们这个家就非常的戏剧性了。

回到计算机

因为你和你的另一半是房子的主人,那么自然可以随意的进出房间,这时如果你去买了一台电脑,你和你的另一半可以去使用它,(电脑在房间里,属于公共资源),你和你的另一半就是线程了,你们想要使用电脑就需要排队了,这就是线程的阻塞了,(当然如果不作死的话,那么理论上女方应该拥有更大的使用权,也就是不公平锁,女方的权重更大,当然公平锁也是可以的,只要不怕老婆削你就ok。)

但是如果不想让熊孩子(线程)去玩电脑(公共资源),那么只要把电脑远离,熊孩子自然不能玩了,此时电脑对于熊孩子来说就是不可访问的权限。

互斥

如果你想使用卫生间,但是有人占据着,那么这个时候理论上你不可以进去,这就是互斥。(因为你要使用公共资源) 别人使用卫生间的时候是从内部上锁(Lock),使用完在解锁(Lock)开门,这个时候你才能使用,卫生间。

但是如果你们家卫生间设计比较独特的话,比如 卫生间的门上有 2 把锁(鬼知道你设计 2 把锁要干嘛,反正都是锁门),那么你把 2 把锁都锁上的话,其实我们就可以看成是 可重入锁了(暂时没想到现实中有什么锁是要锁2次,抱歉)

优先级 & 公平性

早上起床晚了,马上要上班,然后你另一半在卫生间洗漱,而你还好巧不巧的还拉肚子,但是你只能先忍住等着,等你另一半出来,还是阻塞。但是等 他/她 开门后会怎么办,谁接着去呢(如果正巧她还要化妆呢)

如果你已经等了好久了,那么在 另一半出来后,在公平的情况下,应该是你先进去才对,毕竟你等了很长时间了,但是在不公平的情况下呢,那么就要按照优先级了,因为你马上上班迟到,还拉肚子,你是最着急的,而化妆是可以暂缓的,所以你拥有较高的优先级。

线程也一样。线程从其父线程继承其调度算法,但可以调用 pthread_setschedparam() 来更改其调度策略和优先级(如果它有权执行此操作)。

优先级高只是一个建议,比如Java中的高优先级,并不一定会对照 操作系统的优先级,如果操作系统的优先级级大于java的优先级级(10级)还好,要是小于得的话就不行了,这样会导致不同优先级的线程的优先级是一样的。而且 windows 还有一个 优先级推进器,如果一个高优先级的线程执行过多次数,那么会越过优先级来分配。


信号量
  • 信号量为 1 的 我们还是以卫生间为例,其中卫生间的门只有 2 种状态:
    • 门开着,证明没有人在里面
    • 门锁着,证明有人在里面
  • 信号量 大于 1 的 比如家里的钥匙有 2 把。任何拿到钥匙的人都可以打开门进去,当进入到房子里面时,总会锁门,那么外面进入的人总要需要钥匙来开锁。所以如果我们想要控制进入房子里的人,只要控制钥匙就OK了(配钥匙的不算哈。)

线程中就可以使用信号量了,进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。如果多个线程访问同一资源,那么大于 1 的信号量就是允许同一时间的线程通过量了。


内核
  • 内核作为仲裁者

内核确定在特定时刻由哪个线程使用 CPU。并将上下文切换到该线程。所以这里就是个性能问题。

内核切换线程时,需要:

  • 保存当前运行的线程的寄存器和其他上下文信息
  • 将新线程的寄存器和上下文加载到CPU中

内核如何决定另一个线程应该运行?

它会查看特定线程是否能够在此时使用CPU。当遇到互斥锁时,会有一个阻塞状态。我们有一个可以消耗CPU的线程,而另一个不能,因为它被阻塞,等待互斥锁。在这种情况下,内核允许可以运行的线程消耗CPU,并将另一个线程放入内部列表(以便内核可以跟踪其对互斥锁的请求)。


如果有多个线程使用CPU的时候,到底该怎么委派呢?

内核使用优先级和调度算法来评估。

  • 优先级 多个线程中,如果线程具有不同的优先级,那么很简单,内核将 CPU 提供给优先级最高的线程。如果有同一时间一个更高优先级的线程突然变的能够使用 CPU ,那么内核将立即上下文切换,也就是抢占;当高优先级的线程使用完毕后,内核将切换回之前的比较低的线程,这个时候称之为恢复;

但是如果有两个线程有相同的优先级时,又该怎么办呢?请看下方:

  • 调度算法

让我们继续假设,如果一个线程正在使用 CPU,此时内核将检查进行上下文切换的规则(优先级不同的,依然以上面的为主)。此时Linux就要使用调度算法了, Linux 2.6 之后 中主要有楼梯调度算法,CFS (完全公平调度算法) 以及 RSDL(由于内核调度是个大概念,我们这里不再叙述,有兴趣的同学可以下去研究下)。任何时候,实时进程的优先级都高于普通进程,实时进程只会被更高级的实时进程抢占。

线程状态
  • 内核状态
    • 就绪(ready)
    • 执行(running)
    • 挂起(blocked)
  • Linux 进程状态
  • RUNNING

进程正在执行或等待执行

  • INTERRUPTIBLE

进程在休眠模式下等待中断,并等待某些操作可以唤醒此进程。

  • UN-INTERRUPTIBLE

和 INTERRUPTIBLE 相同,只是这个状态的进程无法通过传递信号唤醒

  • STOPPED

进程已停止

  • TRACED

此状态表示正在调试进程

  • ZOMBIE

程已终止但仍在内核进程表中闲置,此进程的父进程仍未获取此进程的终止状态

  • DEAD

终止进程并从进程表中删除条目


  • Java 线程状态
    • NEW(初始化状态)
    • RUNNABLE(可运行 / 运行状态)
    • BLOCKED(阻塞状态)
    • WAITING(无时限等待)
    • TIMED_WAITING(有时限等待)
    • TERMINATED(终止状态

    Linux 进程 & 线程

  • Linux 进程

Linux进程可以被视为运行程序的实例。进程可以使用进程间通信方法与其他进程通信,并可以使用共享内存等技术共享数据。

  • Linux线程

Linux中的线程只是流程的执行流程。包含多个执行流程的流程称为多线程流程。非多线程进程,只有执行流程是主要的,所以也被称为单线程进程

  • 一个整整想了2天的问题, --- Linux 中的线程到底是什么

查询了好多资料,发现好多博客都在说 Linux 不支持线程,只是进程啥的,然后也没有给到具体的理由。我就去内核官网 以及一些 Linux 爱好者博客中找了下,下面是我自己的理解:

在开始的时候 ,也就是 Linux 2.6 版本之前,没有线程,只有单独的进程(具有单独的PID)来抽象线程,这种进程可能具有一些共享资源,如虚拟内存或文件描述符。而这就导致了臭名昭著的LinuxThreads POSIX线程的实现,这是一个误称,因为它没有给出任何与POSIX线程语义类似的东西。

之后 linuxThreads 被 NPTL 取代了,实现发生了变化,NPTL的解决方法与LinuxThreads类似,内核看到的首要抽象依然是一个进程,这种进程称为轻量级进程,新线程是通过clone()系统调用产生的。但是NPTL需要特殊的内核支持来解决同步的原始类型之间互相竞争的状况。在这种情况下线程必须能够入眠和再复苏。

NPTL是一个所谓的1×1线程函数库。用户产生的线程与内核能够分配的对象之间的联系是一对一的。这是所有线程程序中最简单的。。 。轻量级进程(LWP)和正常进程之间的主要区别在于LWP共享相同的地址空间和其他资源(如打开文件等)。由于某些资源是共享的,因此与其他正常进程相比,这些进程被认为是轻量级的因而名称轻量级过程。

在Linux中。内核线程本质上是没有用户空间的进程。用户空间线程是正常的POSIX线程(NPTL)。用户空间进程共享文件描述符,可以共享代码段, 但生活在完全独立的虚拟地址空间中。进程内的用户空间线程共享代码段,静态内存和堆(动态内存),但具有单独的处理器寄存器集和堆栈


这篇我们主要介绍了一些之后会用到的一些基本概念。下篇我们将开始细讲导致我们并发 bug 的三大因素 - 可见性、原子性、有序性 以及 Java 内存模型

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-07-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单线程
  • 多线程
  • 回到计算机
  • 互斥
  • 优先级 & 公平性
  • 信号量
  • 内核
  • 线程状态
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档