关于多线程的优缺点描述正确的有

关于多线程的优缺点描述正确的有使用多线程可能会遇到的问题图解线程在实际开发过程中,错误的使用多线程非但不能提高效率还可能会导致程序崩溃,以在路上开车为例:在一个单向行驶的道路上,每辆车都遵守交通规则,这时候整体通行是正常的,[单向通道]意味着[一个线程],[多辆车]意味着

使用多线程可能会遇到的问题   图解线程   在实际开发过程中,错误的使用多线程非但不能提高效率还可能会导致程序崩溃,以在路上开车为例:   在一个单向行驶的道路上,每辆车都遵守交通规则,这时候整体通行是正常的,[单向通道]意味着[一个线程],[多辆车]意味着[多个job]   
单线程顺利执行   如果需要提升车辆的同行效率,一般的做法就是拓展车道,对应程序而言就是[加线程池],增加线程数,这样在同一时间内,通行的车辆数远远大于单车道   
多车道顺利同行   然而并不会这么完美,车到一旦多起来,[加塞]的场景就会出现,出现碰撞的概览也会变大,从而影响整条马路的通行效率,这么一对比,[多车道]确实可能比[单车道]要慢   
多线程故障   防止车辆加塞,可以增加护栏,而再程序中如何使用   程序中来解决一共可以分为三类:[线程安全问题],[线程活跃问题],[性能问题]   线程安全问题   有时候我们会发现,明明单线程环境中运行的代码,在多线程环境中可能会出现意料之外的结果,其实这就是大家说的[线程不安全问题]   原子性   举一个银行转账的栗子,比如从A转到B账户下1000,那么必然要包含2个操作,从账户A减去1000,往B账户加上1000,两个操作都成功才意味着一次转账的成功   
转账成功   试想一下,如果这两个操作不具备原子性,从A的账户扣减了1000之后,操作突然中止了,账户B没有增加1000,那问题就大了   
转账失败   转账这个例子有两个步骤,出现意外后导致转账失败,说明没有原子性   原子性:即一个操作或多个操作,要么全部执行并且执行过程中不会被任何的因素打断,要么就都不执行   原子操作:即不会被线程调度机制打断的操作,没有上下文切换   在并发编程中很多的操作都不是原子操作,如:   上面这四个操作中哪些是原子性操作,哪些不是?其实只有1是原子操作 操作1:对基本数据类型变量的赋值操作是原子操作 操作2:包含3个操作,读取i的值,将i加1,将值赋给i 操作3:包含2个操作,读取j的值,将j的值赋给i 操作4:包含3个操作,读取i的值,将i加1,将值赋给i   在单线程环境中这4个操作都不会出现问题,但是在多线程环境中,如果不通过加锁操作,往往很可能会出现意料之外的值   在java中可以通过synchronized或者lock来保证原子性   可见性   先上一段代码:   线程1执行update方法将i赋值为100,一般情况下线程1会在自己的工作内存中完成赋值操作,却没有及时将新值刷新到主内存中   这个时候线程2执行get方法,首先会从主内存中读取i的值,然后加载到自己的工作内存中,这个时候i的值还是50,最后返回的值就是50了,原本期望是返回100,这就是可见性问题,线程1对变量i进行了修改,但是线程2没有立即看到i的新值   可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即得到这个修改的值   
可见性问题   如上图所示,每个线程都有自己的工作内存,工作内存和主存间要通过store和load进行交互   为了解决多线程的可见性问题,java提供了volatile关键字,当一个共享变量被volatile修饰时,他会保证修改的值会立即更新到主存,当有其他线程需要读取时,他会去主存中读取新值,而普通共享变量不能保证其可见性,因为变量被修改后刷回到主存的时间是不确定的   当然java的锁机制,如synchronized和lock也是可以保证其可见性的,加锁可以保证在同一时刻只有一个线程在执行同步代码块,释放锁之前会将变量刷回主存,这样就保证了数据的可见性   关于线程不安全的表现还有[安全性]   上面讲到为了解决可见性问题,我们可以采取加锁的方式解决,但是如果加锁使用不当也容易引起其他问题,比如死锁   在说死锁问题之前,我们先引入一个概念:活跃性问题   活跃性是指某件正确的事情终究会发生,当某个操作无法继续进行下去的时候,就会发生活跃性问题   概念比较拗口,看不懂也没关系,可以记住活跃性问题一般有这样几类:死锁,活锁,饥饿锁   死锁   死锁是因为循环依赖而导致程序永远无法继续下去的问题,如图所示:   
死锁问题   活锁   死锁是两个线程都在等待对方释放锁导致阻塞,而活锁的意思是线程没有阻塞,还活着呢   当多个线程都在运行并且修改各自的状态,而其他线程彼此依赖这个状态,导致任何一个线程都无法继续执行,只能重复着自身的动作和修改自身的状态,这种场景就是发生了活锁   其实也可以这么理解:马路中间有条小桥,只能容纳一辆车经过,桥两头开来两辆车A和B,A比较礼貌,示意B先过,B也比较礼貌,示意A先过,结果两人一直谦让谁也过不去   饥饿   如果一个线程无其他异常,但是却迟迟不能继续运行,那基本是处于饥饿状态了   常见的场景: 高优先级的线程一直在运行,消耗cpu资源,所有的低优先级线程一直处于等待 一些线程被永久阻塞在一个等待进入同步快的状态,而其他线程总能在他之前持续的对同步块进行访问   有一个经典的饥饿问题:哲学家用餐问题:如图所示:有5个哲学家在用餐,每个人必须要同时拿两把叉子才可以开始用餐,如果1和3同时开始用餐,那么2,4,5就需要饿肚子等待了   
哲学家就餐问题   性能问题   前面讲了线程安全和死锁,活锁这些问题,会影响多线程的执行,如果这些都没有发生,多线程并发一定会快吗?其实不一定,因为多线程有线程创建和线程上下文切换的开销   创建线程是直接向操作系统申请资源,对操作系统来说创建一个线程的代价是十分昂贵的,需要给他分配内存,列入调度任务等   线程创建完了之后,还会遇到线程上下文切换   
cpu执行   CPU是很宝贵的资源,速度也非常快,为了保证均衡,通常会给不同的线程分配时间片,当CPU从一个线程切换到另外一个线程的时候,CPU需要保存当前线程的本地数据,程序指针等状态,并加载下一个要执行的线程的本地数据,程序指针等,这个切换称之为上下文切换   一般减少上下文切换的方法有:无锁并发编程,CAS算法,使用协程等方式   总结   多线程用好了可以成倍的增加效率,用不好可能比单线程还慢   用一张图总结:   
总结

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/83961.html

(0)
上一篇 2024年 7月 27日 下午10:08
下一篇 2024年 7月 27日 下午10:12

相关推荐

关注微信