警告⚠️:本文耗時很長,先做好心理准備
證明:偏向鎖、輕量級鎖、重量級鎖真實存在
由
【java並發筆記之java線程模型】鏈接: https://www.cnblogs.com/yuhangwang/p/11256476.html這篇文章可知:每當java線程創建的時候相對應的os pthread_create()也會創建一個線程,使用synchronized()就必然調用os pthread_mutex_lock() 函數
synchronized關鍵字鎖的狀態:無鎖、偏向鎖、輕量級鎖、重量級鎖
此篇文章由證明偏向鎖是否存在入手,眾所周知偏向鎖一定會保證線程安全 ,但是實際情況不一定有互斥,偏向鎖是synchronized鎖的對象沒有資源競爭的情況下,不會調用os pthread_mutex_lock() 函數; 但是第一次初始化使用鎖的時候確實會調用一次pthread_mutex_lock()進行偏向鎖
猜想:
偏向鎖一定真實存在
求證方法:
1.修改Linux源碼中glibc庫中pthread_mutex_lock.c文件中的pthread_mutex_lock() 方法,增加輸出當前os id 語句;
2.java代碼中使用synchronized關鍵字加鎖,打印出加鎖前線程id(此線程id會轉化為真實os 線程Id),1和2兩者相互比較;
3.如果調用os pthread_mutex_lock() os-id 與 java thread-id 相同: 說明鎖真的存在, 並且只出現過一次相同為偏向鎖
開始求證:
環境搭建:(此處注意linux內核必須與gilbc庫相對應,否則編譯不成功)
linux:centos 7 Gilbc:Gilbc-2.19 官方gilbc鏈接http://mirror.hust.edu.cn/gnu/glibc/
避免踩坑,個人環境已經上傳百度網盤:請自行下載安裝 百度網盤地址:鏈接:https://pan.baidu.com/s/1LULbCoxm-ooPnZbGG3tdAQ 密碼:9jwu
檢查環境:
一、首先檢查自己的linux有沒有java環境:
通過java、和javac兩個命令查看是否可用
如果沒有則檢查一下yum當中有哪些JDK可以提供安裝命令:
yum search java | grep -i --color jdk
如果沒有安裝, 以jdk8為例:
yum install -y java-1.8.0-openjdk.x86_64 java-1.8.0-openjdk-devel.x86_64
二、
在檢查inux環境要與gIibc版本是否相對應(不對應會導致編譯失敗)
三、檢查有沒有安裝gcc,用來編譯C程序,沒有就安裝gcc
yum -y install gcc
四、解壓glibc
tar -zxvf glibc-2.19.tar.gz
五、修改glibc的源碼
修改:pthread_mutex_lock()該方法,加入打印當前os threadId(也就是調用該方法時打印)
路徑:{your-dir}/glibc-2.19/nptl/pthread_mutex_lock.c---pthread_mutex_lock()
pthread_mutex_lock.c文件下 pthread_mutex_lock() 函數下 第66行修改為:
printf(stderr,”msg tid=%lu\n",pthread_self);
(stderr:文件描述符,pthread_self:當前線程id)
修改完之后,以后所有調用pthread_mutex_lock()函數都會打印出自己的線程id
六、編譯剛才修改完的文件:
cd glibc-2.19
編譯完成后要存放的文件位置:
mkdir out
編譯(完成后覆蓋系統默認的文件,請提前備份默認文件,以防不測)
cd out ../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
執行:
make //make 執行完畢之后再執行 make install
測試 運行:
java
只有調用了pthread_mutex_lock()會打印出自己的線程id:

表示此處修改linux源碼glibc庫下的pthread_mutex_lock()成功;
接下來就該驗證偏向鎖到底是不是真實存在的:
java代碼:
import java.lang.Thread;
public class ThreadTest {
Object o= new Object();
static {
System.loadLibrary( "TestThreadNative" );
}
public static void main(String[] args) {
//打印出主線程
System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxx");
ThreadTest example4Start = new ThreadTest();
example4Start.start();
}
public void start(){
Thread thread = new Thread(){
@Override
public void run() {
while (true){
try {
sync();
} catch (InterruptedException e) {
}
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
while (true){
try {
sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.setName("t1");
thread2.setName("t2");
thread.start();
}
//.c 文件打印出java threaid 對應的os threadid
public native void tid();
public void sync() throws InterruptedException {
synchronized(o) {
//java threadid 是jvm給的線程id 並不是真是的os 對應的線程id
//System.out.println(Thread.currentThread().getId());
//獲取java thread 對應的真實的os thread 打印出id
tid();
}
}
}
獲取真實線程id(os 線程Id)的c文件:(對應上邊java代碼的 native void tid())
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include "Example4Start.h"
JNIEXPORT void JNICALL Java_Example4Start_tid(JNIEnv *env, jobject c1){
printf("current tid:%lu-----\n",pthread_self());
usleep(700);
}
然后再走一遍生成.class、.h 、so 然后執行(此方法在
【java並發筆記之java線程模型】鏈接: https://www.cnblogs.com/yuhangwang/p/11256476.html中有相對應執行方法,請參考)
執行:
java ThreadTest
顯示:


實錘:紅色的證明偏向鎖真實存在,綠色的證明synchronized 1.6之后偏向鎖做了優化(只調用了一次os 函數,后面沒有調用)
同理輕量級鎖及重量級鎖也可以這樣證明出來
原創不易,轉載請標明出處,謝謝
