并发编程
很早以前的计算机上只能执行一个程序,在该程序执行时,下一个执行流只能等待该程序执行结束,我们认为这种依次执行的方式十分浪费资源且效率低下(因为一个程序执行只会消耗计算机的部分资源,其他资源同一时刻得不到充分利用),所以后来引入了并发编程(在宏观上,计算机可以同时执行多个程序)。
多进程和多线程的引入
为了可以很好的进行并发编程操作,引入了多进程操作;进程是操作系统分配资源的基本单位,进程一定的解决了并发编程的操作,但是多进程相对于多线程来说又显得“更重”,(这里的重体现在创建,销毁,调度一个进程的开销更大),多进程在资源分配和回收上不如多线程。
多进程和多线程的关系
- 进程包含线程,一个进程至少包含一个线程(也称作主线程)
- 多进程创建,销毁和调度一个进程的开销更大(主要体现在资源分配和回收上);
举一个例子:
假设有一个工厂通过购入材料进行生产然后卖出设备,该工厂有一个完整的货物运输渠道和一个完整的生产线,如图所示:
假设工厂的生意很好,原来的单一生产线已经供不应求需要扩充,现在有两套方案
方案一:创建一个新的共产,对原来的物流渠道和生产线都进行1:1的扩充,如图所示:
方案二:在原来的工厂内直接加一个完整的生产线,如图所示:
总结:
其中方案一对应了多进程的操作,方案二对应了多线程的操作,方案一不仅在原有的基础上加入了生产线加入了新的物流渠道同时也创建了新的工厂,方案二保留了原来的物流渠道和工厂,仅仅加入了一个新的生产线,所以方案二相对于方案一来说省下了很多的开销;对应到操作系统中,多线程只需要第一次创建线程的时候需要向系统申请资源即可,后来创建的线程都是可以重复利用第一份资源,多进程则每一次创建进程时都需要再次申请资源(申请资源也需要花费时间),故开销更大; - 通过举得这个例子也可以看出进程与进程之间资源几乎不共享,同一个进程下面的线程与线程之间资源是共享的;
- 进程是操作系统分配资源的基本单位,线程是操作系统调度的基本单位,故以后说到调度都指的是对线程的操作;
- 线程使用PCB来描述的,同一个进程下面的线程的pid是相同的。
多线程可能存在的问题
举一个例子:滑稽老铁吃烤鸡(每一个滑稽老铁代表一个线程,烤鸡代表他们所共享的资源)
- 增加线程数量会不会提高效率,如图所示:
我们发现一直增加滑稽老铁数量时,此时桌子已经坐不下了,对应到操作系统中,CPU的核心是有限的,CPU调度线程需要时间开销,线程太多的情况往往会因为开销太大降低效率,同时创建线程也需要消耗很多资源,太多的线程会吃满系统的带宽; - 假设滑稽老铁1号和2号同时看上了鸡大腿,都伸手去拿,可能会打起来,线程1和线程2都会崩溃,而在多进程的时候不会出现这种情况(因为每个进程所享受的资源CPU提前已经分配好l);
- 假设滑稽老铁1号抢走了鸡大腿,滑稽老铁2号生气掀桌了,造成后面的滑稽老铁什么都没吃到,此时整个进程里的所有线程都会崩溃。
总结:
多线程相对于多进程来说提高了效率,但是多线程可能存在的线程安全问题也更多,但是Java开发中我们会更多的选择多线程的模式,这就需要我们开发者写出合理的代码来避免线程安全问题的出现。