什麼?你竟然用Integer作為synchronized的鎖對象?


什麼?你竟然用Integer作為synchronized的鎖對象?

在使用多線程編程時,往往會使用一些手段保證線程安全,也就是加鎖,但是加鎖也必須合理,如使用synchronized對對象加鎖時,如果不注意,還可能發生錯誤的加鎖。

先看一段小測試,在這個小測試中,啟動了1000個線程,每個線程在對integer加1前都先獲得integer的鎖,這看似是線程安全的,並且預期可能會得到1000這個值,而然並不然,在運行多次之後他總是輸出<=1000的值,那麼,這犢子是哪出問題了。

<code> static  Integer integer =new Integer("0");
static CountDownLatch mCountDownLatch =new CountDownLatch(1000);
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=1000; i++) {
new Thread(()->{
synchronized (integer){
integer++;
mCountDownLatch.countDown();
}
}).start();
}
mCountDownLatch.await();
System.out.println(integer);
}
/<code>

這其實就是上述所說:"錯誤的加鎖對象",也就是Integer作為鎖對象這是個不正確的選擇。

那在說Integer,不少人可能會見過這樣的題。

<code>Integer i1 =125;
Integer i2 =125;
System.out.println(i1==i2);

Integer i3 =135;
Integer i4 =135;
System.out.println(i3==i4);
/<code>

答案是true,false,這樣的結果是由Integer的緩存導致,在直接對Integer=xxx時候,其實調用了Integer.valueOf,(可以使用javap命令反編譯查看)

什麼?你竟然用Integer作為synchronized的鎖對象?

而Integer.valueOf中,如果參數值在一定範圍內,就從IntegerCache緩存中返回,也就是說在一定範圍內多個相同的值是同一個對象,超出的話則return new Integer(i)返回一個新的Integer。而這個範圍在-128-127,

什麼?你竟然用Integer作為synchronized的鎖對象?

在上面的135顯然不在範圍,則返回的是新對象,又由於==是比較內存地址,所以上述會出現false。如果要比較包裝類是否相等,正確的做法是equals方法,Integer也重寫了equals,判斷內部基本數據類型是否一樣。

什麼?你竟然用Integer作為synchronized的鎖對象?

這樣一來,synchronized(integer)原因就知道了, integer++是創建了一個新的Integer對象,並將引用賦值給integer。因為integer一直在變,線程每次加鎖可能都加在了不同對象的實例上,導致臨界區控制出現問題。

解決辦法也很容易,只要加在一個不變的對象上即可。

<code>static  Integer integer =new Integer("0");
static CountDownLatch mCountDownLatch =new CountDownLatch(1000);
static Object sObject= new Object();
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=1000; i++) {
new Thread(()->{

synchronized (sObject){
integer++;
mCountDownLatch.countDown();
}
}).start();
}
mCountDownLatch.await();
System.out.println(integer);
}
/<code>

(不只Integer,Character、Short、Long也有緩存,但是Float和Double卻沒有)

關注我,後續更多幹貨奉上!


分享到:


相關文章: