2.synchronized同步鎖
原文鏈接:http://blog.csdn.net/zteny/article/details/54863391
簡介
synchronized是Java語言的一個關鍵字,用來修飾一個方法或者代碼塊,使得目標達到線程同步的目的。
當我們希望某個方法或者代碼塊,同一時間只能有一個線程能夠執行,即是同一時間只有一個線程能夠進入該方法或者代碼塊,其它線程將會被阻塞直接原線程執行結束,此時我們使用該關鍵字。
用法
由上面的簡介可以可得synchronized的兩種用法,如下:
public synchronized void bar() {
...
}
public void foo() {
synchronized(this) {
...
}
}
1. 修飾方法
當用synchronized修飾方法時,此時其功能相當於
public void foo() {
synchronized(this) {
...
}
}
為此,我還特意看來了一下java生成class文件,發現對這兩份代碼生成字節碼並不一樣。那們為什麽會這兩種方式,我個人認為原因有二,當synchronized(this)的範圍從方法的第一行到最後一行時,直接用synchronized修飾方法就是一種偷懶方式;其次,synchronized(this)可以有控制更小的粒度,既是只要圈住需要同步的代碼可以減少的鎖持有時長,提高TPS。
如果您有更準確解釋,希望您能在文後評論,謝謝
2. 修飾代塊碼
通過修飾代塊碼方式來實現同步的目標時,相比修飾方法有兩個優勢。
1. 只需要圈住(保護)應該圈住的代碼塊。被圈住的部分才會同步,其它使臨界區的訪問盡可能的短,從獲得更好的性能。
2. 通引用傳入的變量作為同步的標量,它允許同一時刻有多個線程同時進入同步塊,當它的變量值不同時。反過來,同一時刻且同一個變量值,只允許一個線程進入同步塊。
private Object v = new Object;
public void foo() {
synchronized(v) {
System.out.println(Thread.currentThread().getId() + ", enter");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) { }
System.out.println(Thread.currentThread().getId() + ", leave");
}
}
public void bar() {
new Thread(() -> {
foo();
}).start();
new Thread(() -> {
foo();
}).start()
}
它結果應該是(發生阻塞)
0, enter
0, leave
1, enter
1, leave
接下來我們稍微變一下,會出現一個不同的結果
public void bar() {
new Thread(() -> {
v = "123";
foo();
}).start();
new Thread(() -> {
v = "321";
foo();
}).start()
}
它結果是不發生阻塞的
思考
- 當傳入的是
v = 1
會怎麽這樣呢? -
當引用的是一個靜態不可變變量(即static final Object v = new Object())?
提示1,
Integer a = 1; Integer b = 1;
此時,a == b
為true。當Integer在-128~127是全局都是同一個引用變量。
提示2,ClassLoader。
FAQ
如下這些細節找不到引入理由但又感得這些內容非常有價值,便用FAQ的方式強行帶入。
- 能否在synchronized修飾的方法或代碼塊中發生線程上下文切換?
——能。 - 簡述synchronized具有可重入性。
——在synchronized的方法或代碼塊內可以調用另一個帶有synchronized的方法或代碼塊,而不發生死鎖。 - 所有變量v寫操作都發生synchronized代碼塊裏,此時如果讀操作不在synchronized代碼塊裏,會怎麽樣呢?
——此時變量v為弱一致性。
2.synchronized同步鎖