併發程式設計1
併發程式設計1
編譯openjdk原始碼
- 怎麼看這個方法,在github上下載open jdk方法
- 為什麼下的是jdk11,編譯jdk11必須要有jdk8的程式碼(編譯jdk原始碼,必須在目錄下有一個比它版本低的jdk原始碼)
- native方法是通過JNI方法呼叫的,對應於一個c語言方法,hotspot大部分基礎功能都通過openjdk開源了
java中的執行緒和作業系統的執行緒是什麼關係
-
java執行緒的本質
-
start方法啟動後,呼叫軟方法run()
start()原始碼的執行緒控制原語
- start0()是一個native方法,對應於openjdk中的Thread.c(對應於Thread.java),關聯到 jvm_startThread()方法
jvm_startThread()在jvm.cpp
- 例項化一個c++物件 JavaThread
- MutexLocker()是一個很重要的方法
- 然後根據作業系統型別,如果是linux,就會呼叫os_linux.c下面pthread_create()方法
pthread_create()
-
在os_linux.cpp檔案裡面
-
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
具體的引數
-
根據man配置的資訊可以得出pthread_create會建立一個執行緒,這個函式是linux系統的函式,可以用C
-
pthread_t *thread:傳出引數,呼叫之後會傳出被建立
執行緒的id,定義 pthread_t pid; 繼而 取地址
&pid -
const pthread_attr_t
*attr: 執行緒屬性,關於執行緒屬性是linux
的知識,一般傳NULL,保持預設屬性 -
void(start routine)(void *):執行緒的啟動後的主體函式,會回撥java執行緒中的run()方法,需要你定義一個函式,然後傳函
數名即可 -
void *arg:引數,非必須
linux上啟動一個執行緒的程式碼
-
//標頭檔案 #include <pthread.h> #include <stdio.h> //定義一個變數,接受建立執行緒後的執行緒id pthread_t pid; //定義執行緒的主體函式 void* thread_entity(void* arg) { printf("i am new Thread! from c"); } //main方法,程式入口,main和java的main一樣會產生一個程序,繼而產生一個main執行緒 int main() { //呼叫作業系統的函式建立執行緒,注意四個引數 pthread_create(&pid,NULL,thread_entity,NULL); //usleep是睡眠的意思,那麼這裡的睡眠是讓誰睡眠呢? //為什麼需要睡眠?如果不睡眠會出現什麼情況 usleep(100); printf("main\n"); }
總結
-
下載一個clion的idea
-
為什麼下的是jdk11,編譯jdk11必須要有jdk8的程式碼,下載11、12的程式碼
-
idea中進行debug,配置選項
- debug argument :com.shadow.app.ThreadDemo
-
需要把自己編譯的類放到虛擬機器中的bin目錄下
-
debug過程中,會啟動很多執行緒,所以跳過一些斷點,才會跳到os_linux.cpp()中的pthread_create()是由自己編譯的類實現的
-
aqs比sysnchronized的效率高
- aqs壓根就沒有調過c++的程式碼,除非到park執行緒的階段,這樣才叫重量鎖!而cas這種沒有執行緒切換的過程是輕量鎖。
-
啟動過程
- Thread.start()
- native Thread.start0()
- JVM 例項化一個c++物件JavaThread(JVM_ENTRY–巨集)
- 判斷os,如果是linux系統,會呼叫pthread_create()
實現JNI呼叫
-
package com.shadow.app; public class ZLThread { //裝載庫,保證JVM在啟動的時候就會裝載,故而一般是也給static static { System.loadLibrary( "EnjoyThreadNative" ); } public static void main(String[] args) { ZLThread zLThread =new ZLThread(); // tnew.c.start1() zLThread.start1(); } private native void start1(); }
-
自定義JNI對應的java程式碼已經有了,還需要些c語言的程式碼
啟動步驟
-
1.javah編譯一個so檔案
-
javah在java9之後就沒有了
-
java11之後javac ZLThread.java
javac -h . ZLThread.java(java11中間有一個點,要注意)
-
會多一個com_shadow_app_ZLThread.h檔案
-
#include<jni.h> #ifndef _Included_com_shadow_app_ZLThread #define _Included_com_shadow_app_ZLThread #ifdef __cplusplus extern "C"{ #endif JNIEXPORT void(JNICALL Java_com_shadow_app_ZLThread_start1(JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
-
-
2.需要把上一步生成的.h檔案中的方法名複製到自己的.c檔案中
-
本地方法的程式碼編寫( tnew.c檔案)
-
#include <pthread.h> #include <stdio.h> #include "com_shadow_app_ZLThread.h" //定義變數接受執行緒id pthread_t pid; //執行緒的主體方法相當於 java當中的run void* thread_entity(void* arg) { //子執行緒死迴圈 while(1){ //睡眠100毫秒 usleep(100); //列印 printf("I am new Thread\n"); } } //c語言的主方法入口方法,相當於java的main JNIEXPORT void(JNICALL Java_com_shadow_app_ZLThread_start1(JNIEnv *, jobject) { //呼叫linux的系統的函式建立一個執行緒 pthread_create(&pid,NULL,thread_entity,NULL); //主執行緒死迴圈 while(1){ //睡眠100毫秒 usleep(100); //列印 printf("I am main\n"); } }
-
-
3.把.c檔案轉換成.so檔案
-
gcc -fPIC -I /usr/lib/jvm/java-1.8.0-openjdk/include -I /usr/lib/jvm/java-1.8.0-openjdk/include/linux -shared -o libLubanThreadNative.so threadNew.c
-
gcc -fPIC -I /home/shadows/soft/jdk11/include -I /home/shadows/soft/jdk11/include/linux -shared -o libEnjoyThreadNative.so tnew.c
-
-
4.需要把.so檔案所在目錄加到linux需要載入的資源目錄中,這樣java檔案才能找到libEnjoyThreadNative.so檔案
-
export LD_LIBRARY_PATH=&LD_LIBRARY_PATH:{libLubanThreadNative.so}
-
export LD_LIBRARY_PATH=&LD_LIBRARY_PATH:/home/shadows/enjoy/com/shadow/app
-
-
5.執行java檔案
- java com.shadow.app.ZLThread
- 啟動成功的話,死迴圈列印I am new Thread和I am main
答疑
1.java中的執行緒內容是跟作業系統中的執行緒對應的
2.怎麼解決事務補償
-
訂單->支付-(mq)->發紅包->加積分
-
假設發紅包掛掉了,肯定不能回滾,使用者錢都沒了
-
等服務起來,去做事務補償
-
通過冪等解決(某一個方法有10行,運行了5行,在這裡掛掉了,服務恢復後,不能重複前5行)