近日在学习Java的并发编程,接触到一些新的概念,开始比较晦涩,就想到用一个例子来生动地解释一下,或许可以帮助到初学者来理解。
并发编程,与顺序编程相反相成。并发指的是,有多个程序同时处于开始执行和执行完毕的状态之间。线程,是分配CPU时间的最小单位,用以驱动程序块的执行。进程,是运行在它自己的地址空间内的自包容的程序。
说人话,用比喻来理解这个问题:
我们用马来代指线程,用马厩来代指线程池,用牧马人来代指CPU。
作为一匹马,可以执行拉车、拉磨、耕地、载人等各种任务,但是必须要有人来指挥管理才能正常执行。人可以给同一匹马分配多个任务,也可以分配单个甚至不分配任务:相当于每一个线程都有自己的任务Runnable序列。每一匹马会将车从头拉到尾、或者把一担谷子在磨盘上磨完再执行下一个任务:线程总是把一个任务队列中的一个任务完成,才执行下一个任务。人需要照顾管理所有正在执行任务的马,只有人在场,马才能正常工作,否则它们都会偷懒,除非有多个牧马人来管理——每一个时刻只有一个线程被CPU执行,其他线程就被挂起,除非有多个CPU。每当来了新任务,人要么将这个任务放到一匹马的任务序列中,要么从马厩中另外拉一匹马出来执行这个任务:从线程池取出线程执行任务。
当没有任务时,马厩被暂时解散,马厩里的马都放归牧场:线程池被shutdown。新来了任务,人就从牧场上赶马进入马厩:生成线程池。赶马进马厩的时候,你可以一开始就确定好赶几匹马进来,比如5匹马——Ececutors.newFixedThreadPool(5) ,或者只要一匹马——Executor.newSingleThreadExecutor()。假如开始不知道要几匹马,就用Executor.newCachedThreadPool()来为你服务,当然这要增加CPU开销——你得时刻准备着去牧场找马。
进程,就是一个完整的、封闭的任务。比如,用马拉车是一个任务,用马把小麦拉到磨坊、再拉磨给小麦磨成面粉、再把磨好的面粉拉到集市上卖掉也是一个任务。前者用一匹马比较简单,因为只要套一次车,换马的话要套车、解套再套到另外一匹马身上,所以这种任务常常用一个线程就能很好地解决。后者,则需要好几匹马来做,所以用多个线程来驱动会更好。
锁,是用来保证对象在一个进程对它的访问结束之前,专属于此线程的工具。比如,一包小麦被马拉到磨坊去,只有拉到了,磨坊里的马才能拉磨磨面粉;也只有磨面粉完成了,面粉才能被拉到集市上出售。如果没有锁,可能会出现石磨空转、小麦和面粉混合在一个袋子里的情况。
线程间的通信,是为了相互通知情况。比如,一匹马把小麦拉到磨坊了,铃铛会响,告知磨坊的马可以拉磨了——用notify或者notifyAll来告知正在wait的线程可以尝试采取动作了。
用这个比喻简单地描述了一下多线程编程的一些基本概念,比喻可能不太恰当,欢迎大家交流。