协程虽然被提出的时间很早,但是使用它的年限很短。尤其是最近几年,随着 Go、Lua 等语言的流行,把协程推向了一个新的高潮。
在所有语言中都存在着层级调用,比如 A 调用 B,B 在执行过程中又调用了 C,C 执行完毕返回,B 执行完毕返回,最后是 A 执行完毕。
这种方法、函数、子程序(或者称为函数,与方法一样,只是不同的叫法)的调用方式都是是通过栈实现的,一个线程就是执行一个子程序。
子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。
例如,我现在有两个方法 a 和 b,代码如下:
上面代码如果由协程来执行,那么在执行 a 的过程中,可以随时中断,去执行 b,b 也可能在执行过程中中断再去执行 a,所以,最终的结果可能是:
但是,在上面的代码中,并没有在 a 方法中调用 b。执行结果就像两个线程在并发执行。但其实,通过协程执行用的是一个线程,只不过这个线程看起来有点“到处乱跑”。
协程和线程相比,有三个比较明显的优势。
1、减少了线程切换的成本。Java 中的线程,不管是创建还是切换,都需要较高的成本。子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。这也就是说,协程的效率比较高。
2、协程的第二大优势就是,不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
3、协程更轻量级。创建一个线程栈大概需要 1M 左右,而协程栈大概只需要几 K 或者几十 K。
有优势也有劣势,因为前面的程序看起来在“上串下跳”,所以,协程看起来也没那么好控制。
但是不管怎么说,越来越多的语言都在引入协程。Java 中的 OpenSDK 中的 Loom 项目就是为协程而生。甚至可以幻想一下,不久的将来,Java 一定会拥抱协程。