并发编程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行)