警告⚠️:本文耗時很長,先做好心理準備
證明:偏向鎖、輕量級鎖、重量級鎖真實存在
由【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 函式,後面沒有呼叫)
同理輕量級鎖及重量級鎖也可以這樣證明出來
轉載請標明出處