我们分别来看看java和golang实现并发任务的机制,java里面是Thread,golang里面是goroutine。比如你想创建一个发邮件任务,一个接受消息任务,对应的就是java创建2个独立线程,golang创建2个独立的goroutine。
java和golang的程序最终都是在操作系统上执行的,以linux操作系统为例,操作系统有自己的多线程机制,调用
clone(2) - Linux manual pageclone函数就可以创建一个线程。
java线程和操作系统是一对一的关系,也就是说当你创建一个java线程的时候,实际上系统同时创建了一个系统线程来跑该java线程,同理java的所以的线程操作都是和系统线程有对应的实现,比如sleep啥的。
随着高并发时代的到来,通常一个系统通常会有成千上万个任务要同时执行,理论上要创建成千上万个线程分别来执行每个任务,但通常你的电脑只有几个cpu,如果这样,就会有上万个线程来分享这几个cpu,这就涉及到切换线程来占用cpu,每次切换动作,需要保存上一个线程的context(上下文)然后把下一个线程的上下文提出出来给cpu。这样会有啥问题呢?上面说道java的线程是系统原生的线程,所以java线程的切换都是系统级别的线程切换,这样每次切换除了要保存和提取,重要的是进程要从用户态切换系统态下来完成,总之啦,就是每次线程切换都很耗费资源啦。
如果真的有很多线程同时跑在系统上,很多资源都耗费在现场切换上了,java通常上了2k个线程就受不了啦,不知道现在咋样了。所以啊,java这种线程和系统线程一一对应的关系就有些问题啦。
不过知道了问题的所在是操作系统的线程耗费资源,就应该让用户的任务和系统线程不要一一对应了吧。于是就有了threadpool,一个线程池少数几个线程对应多个任务,但是这样任务的状态啥的要你自己维护了,so,写这样的代码就需要功力啦,所以呀现在会并发编程的码农可值钱啦。
那有人就说,如果一种机制可以让语言的线程和系统的线程多对一,这样可以一个任务对应一个语言线程,而很少几个系统线程来处理这几个语言线程,该多好啊。哈哈,大牛们早就想到啦,所以提出了协程(routine)的概念(也叫超线程,就是线程上的线程),采用的切换算法就是work-steel算法。
说道这里,你应该明白了吧,goroutine就是我们说的协程(routine),他能让你采用很简单的编程模型编写能处理高并发的程序,这就是goroutine的好处。
总结一下,java线程和系统线程一一对应,而goroutine和系统线程多对一。