synchronized鎖:①鎖實體 ②鎖物件
package com.hx.zbhuang;
public class SyncExplore {
public void test(){
// 鎖住類實體
synchronized (this){
System.out.println("this start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("this end");
}
}
public synchronized void test1(){
// 鎖住類實體
System.out.println("method this");
}
public void test2(){
// 鎖住類物件
synchronized (this.getClass()){
System.out.println("Class start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Class end");
}
}
public synchronized static void test3(){
// 鎖住類物件
System.out.println("method Class");
}
public static void main(String[] args) {
SyncExplore syncExplore = new SyncExplore();
new Thread(syncExplore::test,"t1").start();
new Thread(syncExplore::test1,"t1").start();
new Thread(syncExplore::test2,"t1").start();
new Thread(SyncExplore::test3,"t1").start();
}
}

持有相同鎖的代碼塊同步執行

synchronized鎖標識為物件頭的運行時元資料的執行緒持有鎖
http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html

每個GC托管堆物件開始處的公共結構,(每個oop指向一個物件頭)包括關于堆物件的布局、型別、GC狀態、同步狀態和標識哈希代碼的基本資訊,由兩個詞組成,在陣列中,它后面緊跟一個長度欄位,Java物件和VM內部物件都有一個通用的物件頭格式,

每個物件頭的第一個欄位,通常是一組位域,包括同步狀態和標識哈希碼,也可以是指向同步相關資訊的指標(具有特征低位編碼),在GC期間,可能包含GC狀態位,

每個物件頭的第二個欄位,指向另一個描述原始物件布局和行為的物件(元物件),對于java物件,“KLASS”包含C++樣式“VTABLE”,
引入依賴:
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
創建一個物件查看物件頭資訊:
package com.hx.zbhuang;
public class DouFuDan {
boolean flag = false;
}
package com.hx.zbhuang;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* 物件頭hash由二進制轉換為十六進制
*/
public class HashUtil {
public static void countHash(Object object) throws NoSuchFieldException, IllegalAccessException {
// 反射獲取theUnsafe屬性(new Unsafe())
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
long hashCode = 0;
//獲取去除MarkWord前8位(鎖標志位)后56位二進制(hashCode)
for (long index = 7; index > 0; index--) {
hashCode |= (unsafe.getByte(object, index) & 0xFF) << ((index - 1) * 8);
}
//轉換為十六進制
String code = Long.toHexString(hashCode);
System.out.println("util-----------0x"+code);
}
}
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
public class JOLExploreSync1 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
DouFuDan douFuDan = new DouFuDan();
// 獲取jvm資訊
System.out.println(VM.current().details());
System.out.println("before hash");
// 物件進行hashCode
System.out.println(Integer.toHexString(douFuDan.hashCode()));
// 獲取物件頭的hashCode值
HashUtil.countHash(douFuDan);
System.out.println("after hash");
// 獲取物件資訊
System.out.println(ClassLayout.parseInstance(douFuDan).toPrintable());
}
}

可以看出物件由16byte存盤,物件頭占12byte,實體資料占1byte,對齊填充站3byte,其中物件頭中運行時元資料占8byte,型別指標占4byte,運行時元資料8byte中第1個byte存盤分帶年齡,偏向鎖標識,物件狀態,第2-5個byte為hashCode值和1bit存盤unse,第6-8byte為unuse,
運行時元資料8byte中第1個byte存盤:

DouFuDan物件未持有鎖,偏向鎖標識為0,物件狀態為01為無鎖
測驗持有鎖:
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class JOLExploreSync2 {
public static void main(String[] args) {
DouFuDan douFuDan = new DouFuDan();
System.out.println("before lock:" + ClassLayout.parseInstance(douFuDan).toPrintable());
synchronized (douFuDan) {
System.out.println("locking:" + ClassLayout.parseInstance(douFuDan).toPrintable());
}
System.out.println("after lock:" + ClassLayout.parseInstance(douFuDan).toPrintable()); }
}

DouFuDan物件被加鎖后偏向鎖標志位為0,物件狀態位為00,大佬們說這個是輕量鎖,而非偏向鎖為什么呢?
(輕量級鎖嘗試在應用層面解決執行緒同步 問題,而不觸發作業系統的互斥操作,輕量級鎖減少多執行緒進入互斥的幾率,不能代替互斥)
加個休眠時間
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class JOLExploreSync2 {
public static void main(String[] args) {
try {
Thread.sleep(5000);
DouFuDan douFuDan = new DouFuDan();
System.out.println("before lock:" + ClassLayout.parseInstance(douFuDan).toPrintable());
synchronized (douFuDan) {
System.out.println("locking:" + ClassLayout.parseInstance(douFuDan).toPrintable());
}
System.out.println("after lock:" + ClassLayout.parseInstance(douFuDan).toPrintable());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

發現偏向鎖標志位為1,物件狀態位為01,為大佬所說的偏向鎖標識位,這是為什么呢?
查看https://www.oracle.com/java/technologies/java-tuning.html#section4.2.5

Enables a technique for improving the performance of uncontended synchronization. An object is "biased" toward the thread which first acquires its monitor via a monitorenter bytecode or synchronized method invocation; subsequent monitor-related operations performed by that thread are relatively much faster on multiprocessor machines. Some applications with significant amounts of uncontended synchronization may attain significant speedups with this flag enabled; some applications with certain patterns of locking may see slowdowns, though attempts have been made to minimize the negative impact.
啟用一種提高無爭用同步性能的技術,一個物件“偏向”于執行緒,該執行緒首先通過monitorenter位元組碼或同步方法呼叫獲取其監視器;在多處理器計算機上,由該執行緒執行的與監視器相關的后續操作相對要快得多,一些具有大量非競爭同步的應用程式在啟用此標志時可能會獲得顯著的加速;某些具有某些鎖定模式的應用程式可能會出現減速,盡管已經嘗試將負面影響降至最低,
在另一篇探索synchronized偏向鎖與重量鎖區別中修改pthread_mutex_lock發現啟動main行程會有大量執行緒操作os加鎖,如果開始就加上偏向鎖,就會導致減速,可能是jvm底層優化,直接上了輕量鎖
現在我們注釋休眠代碼,添加引數-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0,將偏向鎖延遲時間設定為0發現結果偏向鎖標志位為1,物件狀態位為01,應該是jvm做了優化,

添加引數查看延遲加載時間:

修改大于4000ms鎖競爭測驗:查看結果一致變為偏向鎖
修改DouFuDan
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class DouFuDan {
public synchronized void lock(){
System.out.println("method lock locking:" + ClassLayout.parseInstance(this).toPrintable());
}
}
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class JOLExploreSync3 {
public static void main(String[] args) {
DouFuDan douFuDan = new DouFuDan();
System.out.println("before lock:" + ClassLayout.parseInstance(douFuDan).toPrintable());
new Thread() {
public void run() {
synchronized (douFuDan) {
try {
Thread.sleep(6000);
System.out.println("main DouFuDan locking:" + ClassLayout.parseInstance(douFuDan).toPrintable());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
System.out.println("after main DouFuDan locking:" + ClassLayout.parseInstance(douFuDan).toPrintable());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
douFuDan.lock();
}
}

競爭后偏向鎖標志位為0,物件狀態位為10變為偏向鎖標志位為0,物件狀態位為10,升級為重量級鎖
查看http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp

發現偏向鎖hashCode位置存盤的是javaThread,這導致計算hashCode之后不能設定為偏向鎖,不然計算出來的hashCode值會被覆寫為javaThread,導致兩次計算出來的hashCode不一致
休眠五秒后計算hashCode加鎖
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class JOLExploreSync4 {
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
DouFuDan douFuDan = new DouFuDan();
douFuDan.hashCode();
synchronized (douFuDan) {
System.out.println("locking:" + ClassLayout.parseInstance(douFuDan).toPrintable());
}
}
}

發現偏向鎖標志位為0,物件狀態位為00,輕量級鎖
總結為
| 無鎖 | 偏向鎖標志位為0,物件狀態位為01 |
| 偏向鎖 | 偏向鎖標志位為1,物件狀態位為01 |
| 輕量級鎖 | 偏向鎖標志位為0,物件狀態位為00 |
| 重量級鎖 | 偏向鎖標志位為0,物件狀態位為10 |
查看各種鎖的時間:
修改豆腐蛋
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class DouFuDan {
int i;
public synchronized void add(){
if(i==0) {
System.out.println("method add"+ ClassLayout.parseInstance(this).toPrintable());
}
i++;
}
}
偏向鎖
package com.hx.zbhuang;
public class JOLExploreSync5 {
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
DouFuDan douFuDan = new DouFuDan();
long start = System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
douFuDan.add();
}
long end = System.currentTimeMillis();
System.out.println(String.format("%sms", end - start));
}
}

輕量級鎖
package com.hx.zbhuang;
public class JOLExploreSync6 {
public static void main(String[] args) {
DouFuDan douFuDan = new DouFuDan();
long start = System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
douFuDan.add();
}
long end = System.currentTimeMillis();
System.out.println(String.format("%sms", end - start));
}
}

重量級鎖
修改DouFuDan
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class DouFuDan {
public synchronized void open(){
if(JOLExploreSync7.downLatch.getCount()==0) {
System.out.println("method add"+ ClassLayout.parseInstance(this).toPrintable());
}
JOLExploreSync7.downLatch.countDown();
}
}
package com.hx.zbhuang;
import java.util.concurrent.CountDownLatch;
public class JOLExploreSync7 {
static CountDownLatch downLatch = new CountDownLatch(1000000000);
public static void main(String[] args) {
DouFuDan douFuDan = new DouFuDan();
long start = System.currentTimeMillis();
for(int i=0;i<2;i++){
new Thread() {
@Override
public void run() {
while(downLatch.getCount()>0) {
douFuDan.open();
}
}
}.start();
}
try {
downLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(String.format("%sms", end - start));
}
}

| 鎖型別 | 偏向鎖 | 輕量級鎖 | 重量級鎖 |
| 消耗時間/ms | 4657 | 16425 | 64279 |
wait解鎖
package com.hx.zbhuang;
import org.openjdk.jol.info.ClassLayout;
public class JOLExploreSync8 {
public static void main(String[] args) {
DouFuDan douFuDan=new DouFuDan();
System.out.println("before lock:" + ClassLayout.parseInstance(douFuDan).toPrintable());
new Thread(){
@Override
public void run() {
synchronized (douFuDan){
synchronized (douFuDan){
System.out.println("before wait:" + ClassLayout.parseInstance(douFuDan).toPrintable());
try {
douFuDan.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("after wait:" + ClassLayout.parseInstance(douFuDan).toPrintable());
}
}
}
}.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (douFuDan){
System.out.println("update:" + ClassLayout.parseInstance(douFuDan).toPrintable());
douFuDan.notify();
}
}
}

開始執行緒啟動為輕量級鎖,執行await進行等待釋放鎖,主執行緒執行獲取鎖升級為重量鎖并喚醒子執行緒,后面得到的鎖為重量鎖,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/231546.html
標籤:其他
上一篇:DC-9靶機滲透詳解
