多线程底层原理

多线程底层原理


那,各位之前有没有听过一句话叫做, 一个CPU在同一个时间片只能执行一个程序

什么意思呢?

就是,你的程序是不是都运行在一个CPU上啊,那你真正一个CPU在同一个时间片里是不是只能执行一个程序呀,那这个程序究竟要执行那个程序,是不是就需要通过线程之间时间片的一个争抢;

时间片:微小的时间段;

多线程说白了就是时间片的争夺,那个线程获取了时间片,就执行那个线程的代码;

img

假设,t1线程先获得时间片,那么,t1线程就优先执行;

但是,它不可能拿着那个时间片不放吧,因为在CPU执行的过程中,底层运用轮循制的;

多线程执行的时候,CPU分配时间片是采取轮循的方式进行分配的;

就是轮流,有点像值日的时候,轮流值日一样;

那,CPU在分配时间片的时候,第一个t1先抢占到了之后,他先执行了一段时间之后,CPU把这个t1执行完了以后,CPU是不是接着把时间片分配给t2去执行了;

那事实上,也是t2也在去抢占时间片;

t1执行完毕后,那么,CPU就将迎来新的一轮争夺,这个时候t2抢到了,就开始执行t2的代码;

这就是多线程的底层执行原理;

多线程它在本质上运行的时候,他是同时执行的,还是轮流执行的呢?

肯定不是同时执行的,也就是不是我们常说的并发执行;那在你们看来,实际就是宏观上你来看就是同时执行,但是在微观上是不是的;

线程的状态

线程总共有五种状态;

第一个状态 新建状态

新建状态,就是你新建一个线程是的状态,也就是你新建了一个线程但还没有启动时的状态;

当线程执行start方法的时候,就会进入就绪状态;

第二个状态 就绪状态

进入就绪状态的时候,事实上就是准备抢占CPU的时间片;

一旦抢占到了CPU的时间片它就会立即进入运行状态;

第三个状态 运行状态

当线程抢占到了CPU时间片的时候,它才会运行,所以第三个状态是,运行状态;

在它的运行状态中,还有可能执行一个代码,Throad.sleep();睡眠;

就是在你执行的时候,突然让你睡眠了,我都让你这个线程睡眠了,你还有必要去争夺这个CPU资源吗?

就肯定没有必要再去争夺这个CPU资源了,那这个时候你就需要释放CPU啊,对不对,你释放之后,你下次再运行的时候,你就需要重新获取CPU的时间片,所以这种状态就叫做堵塞状态;

第四个状态 堵塞状态与sleep方法

想让线程阻塞,最常用的方式就是使用sleep,用sleep这个方法,可以使运行中的线程回到就绪状态;

因为它需要重新抢占CPU资源的,所以,sleep状态的最终目的是让改线程回到就读状态;

就比如,我现在想让这个线程,进我想让它每次进入run方法中的for循环打印里写一个睡眠,一遍循环遍历输出,一边睡眠看会发生什么:

img

我在run方法中业务写完后,我们测试一下该线程:

img

在上图中,可以发现,我同时调用了两次start方法,说明,我执行了两次我一次性开启了两次线程,并且执行了两次,我们看看会不会出现交替执行的情况:

img

从输出结果来看,确实交替执行了并且,是俩俩执行的:

img

每过一秒,就会执行一次:

img

我就不继续演示了;

所以,我们从中可以看出,不管哪个线程过来,t1也好t2也好,执行的时候,均睡眠一秒钟,睡眠完一秒钟之后,谁先醒了,谁就继续向下执行,这个就是到点自然醒的;

也可以使用join来造成线程堵塞;

join

刚刚,我们在上面介绍了sleep,我们来看看join;

join():是线程加入

它底层执行的是,当你在执行一个线程的时候,如果遇到其他线程加入,则会先执行加入的线程,直到的加入的线程执行完成,才会继续执行原来线程的任务;

什么意思呢?

就是说,还是上面那个t1,与t2的例子,那假设说,t1在执行的过程中,突然遇到了一个代码t2.join,这时候,就会在这个时间片,优先执行t2的线程;

join()方法可以给一个参数,参数代表执行的毫秒;

要正确的理解

阻塞当前线程直至 *this 所标识的线程结束其执行。

*this 所标识的线程的完成同步于对应的从 join() 成功返回。

*this 自身上不进行同步。同时从多个线程在同一 thread 对象上调用 join() 构成数据竞争,导致未定义行为。

也就是说,在哪里执行join()前提是可以join 就在哪里阻塞

第五个状态 死亡状态

线程执行完了,或因异常退出了,都会结束生命周期,这就是死亡状态;

img

挂起和睡眠是主动的,挂起恢复需要主动完成,睡眠恢复则是自动完成的,因为睡眠有一个睡眠时间,睡眠时间到则恢复到就绪态。而阻塞是被动的,是在等待某种事件或者资源的表现,一旦获得所需资源或者事件信息就自动回到就绪态

睡眠和挂起是两种行为,阻塞则是一种状态。

所以不管是join() ,sleep() wait() 等自己操作的主动行为都是让线程取阻塞(到达某种状态),而这个动作又被分为休眠和挂起

首先这些术语都是对于线程来说的。对线程的控制就好比你控制了一个雇工为你干活。你对雇工的控制是通过编程来实现的。

挂起线程的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。

使线程睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。

线程阻塞的意思就是,你突然发现,你的雇工不知道在什么时候没经过你允许,自己睡觉呢,但是你不能怪雇工,肯定你这个雇主没注意,本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。至于扫帚回来后,雇工会不会知道,会不会继续干活,你不用担心,雇工一旦发现扫帚回来了,他就会自己去干活的。因为雇工受过良好的培训。这个培训机构就是操作系统

线程分配

  1. 任务并行 : 每个线程执行代码的一部分
  2. 数据并行 : 每个线程处理一部分数据
  3. 线程间潜在问题就是修改共享数据,致使不变量遭到破坏;只要系统中的不变量不受什么影响就不会出现什么问题.如果会产生不好的

标不标记为互斥都是根据代码来说的。

只要是调用函数的实体没有被成功上锁(比如说函数返回引用),那么实体调用函数部分的代码就是没有被成功上锁的。

切勿将受保护数据的指针或引用传递到互斥锁作用域之外,无论
是函数返回值,还是存储在外部可见内存,亦或是以参数的形式传递到用户提供的函数中去。其实所谓的锁也就是对于互斥量的获取

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值