Kilim是一個Java的actor框架,讓你能夠在JVM裏使用基於協程的actor模型,bluedavy曾經介紹過,這裏再也不贅言。這篇blog的目的在於分析下kilim實現的基本原理,看看怎麼在JVM上實現協程。java
在一些語言層面上支持協程的語言,如lua、ruby,都是直接在VM級別支持協程,VM幫你作context的保存和恢復。JVM沒有提供這樣的指令來保存和恢復方法棧的狀態,所以kilim的實現仍是須要在bytecode級別作文章。首先,試想下,若是是你來實現協程,你會怎麼作?協程的兩個基本原語resume和yield,resume運行協程,yield讓出執行權,下次resume的時候會從yield的地方從新執行,而且context保持不變。可見,你須要作這麼幾個事情:
一、在yield的時候保存當前context。
二、在resume的時候恢復context,並根據pc計數來決定從哪裏恢復執行。
三、半協程的實現來講,還須要一個調度器來調度全部協程。
四、爲了作到用戶代碼透明,可能須要某種手段去修改用戶代碼,自動幫你作上面三個事情。ruby
kilim的實現就是幹了這麼幾個事情:
一、利用字節碼加強,將普通的java代碼轉換爲支持協程的代碼。
二、在調用pausable方法的時候,若是pause了就保存當前方法棧的State,中止執行當前協程,將控制權交給調度器
三、調度器負責調度就緒的協程
四、協程resume的時候,自動恢復State,根據協程的pc計數跳轉到上次執行的位置,繼續執行。框架
下面是來自kilim文檔的一個例子,同一段代碼,在字節碼增前先後的變化:
ide
左邊是原始代碼,右邊是經過字節碼加強後的代碼。其中h方法是pausable的,也就是說可能被暫停阻塞的,g方法由於調用了h這個方法也變成了pausable。lua
首先看,原始的h方法是沒有傳入任何參數的,加強後的代碼,多了個參數Fiber,指向當前的協程,一樣,g方法原本只有一個參數n,如今在後面也多了個Fiber類型的參數,一樣是指向當前執行的協程。翻譯
其次,在原始的g方法裏,一旦調用,立刻進入一個for循環。可是在加強後的代碼,多了個switch派發的過程,這就是前面提到的,根據當前的Fiber的pc計數,跳轉到上一次執行的地方執行。若是是第一次resume,也就是啓動協程,那麼就將初始循環的i設置爲0,進入原始代碼的循環部分。Fiber有一個pc計數,稱爲程序計數器,用於指向恢復context的時候須要跳轉到位置。code
第三,在g方法裏調用h這個可被暫停阻塞的方法的時候,在h方法先後多了一些調用:協程
[java] f.down(); h(f); f.up(); [/java]
kilim的Fiber將每一個pauseable方法的調用組織成一個棧,每一個pauseable方法都有一個activationframe,翻譯過來能夠稱爲活動棧幀,這個棧幀記錄了當前的棧的State,注意這個棧跟java自己的方法調用棧區分開來,一個是VM層面的,一個是kilim框架層面的。這裏的down方法就是將棧向下延伸,表示將調用一個pauseable方法,而且設置當前State和pc計數。
調用了down以後,纔是調用實際的h方法,最後還要調用一次up,顧名思義,就是說一次pauseable方法調用完成,fiber的活動棧要遞增一層,回到上一層。可是h方法調用可能出現四種狀況:
一、正常的順利返回,沒有狀態須要恢復,所謂NOT_PAUSING__NO_STATE
二、也是正常返回,有狀態須要恢復,也就是NOT_PAUSING__HAS_STATE
三、h方法暫停阻塞,當前沒有保存狀態,須要保存狀態,這是第一次暫停的時候,稱爲PAUSING__NO_STATE
四、h方法暫停阻塞,當前已經有狀態,不須要保存狀態,這是第一次暫停以後的resume再次暫停,稱爲PAUSING__HAS_STATE,一般不須要處理什麼。blog
第四,能夠看到,在up以後,就要根據up返回的上述4種狀態執行不一樣的邏輯:文檔
[java] if (f.isPausing){ //第一次暫停,沒有狀態 if (!f.hasState){ //new一個State_I2,並保存i和n f.state = new State_I2(i,n); //記錄pc,還記的前面的switch嗎? f.pc = H1; } return; } else if (f.hasState) //正常返回,有狀態須要恢復,恢復i和n State_I2 st = (State_I2) f.state; i = st.i1; n = st.i2; } [/java]
這裏沒有處理NOT_PAUSING__NO_STATE和PAUSING__HAS_STATE,由於這兩種狀況在這裏不須要處理。
經過上面的分析,我想你們對kilim的實現應該已經有一個很基本的認識。下一步,咱們分析一個實際的代碼例子,查看整個運做流程。