1. 程式人生 > 其它 >併發程式設計1

併發程式設計1

技術標籤:併發程式設計java

併發程式設計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

    或者C++直接呼叫,上面資訊也告訴程式設計師這個函式在pthread.h, 這個函式有四個引數,這四個引數由glibC提供

  • 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行)