Java锁机制(一)synchronized
?
進(jìn)行多線程編程的時(shí)候,需要考慮的是線程間的同步問(wèn)題。對(duì)于共享的資源,需要進(jìn)行互斥的訪問(wèn)。在Java中可以使用一些手段來(lái)達(dá)到線程同步的目的:
1. synchronized?
2. ThreadLocal,線程本地變量
3. Java.util.concurrent.Lock
Java中,線程會(huì)共享堆上的實(shí)例變量以及方法區(qū)的類變量,而棧上的數(shù)據(jù)是私有的,不必進(jìn)行保護(hù)。synchronized方法或synchronized塊將標(biāo)記一塊監(jiān)視區(qū)域,線程在進(jìn)入該區(qū)域時(shí),需要獲得對(duì)象鎖或類鎖,JVM將自動(dòng)上鎖。synchronized提供了兩種主要特性:
1. 互斥。互斥是指一次只允許一個(gè)線程持有某個(gè)特定的鎖,因此可使用該特性實(shí)現(xiàn)對(duì)共享數(shù)據(jù)的并發(fā)訪問(wèn),保證一次只有一個(gè)線程能夠使用該共享數(shù)據(jù)。
2.可見(jiàn)性。確保釋放鎖之前對(duì)共享數(shù)據(jù)做出的更改對(duì)隨后獲得該鎖的另一個(gè)線程是可見(jiàn)的。如果不能保證可見(jiàn)性,也就無(wú)法保證數(shù)據(jù)正確性,這將引發(fā)嚴(yán)重問(wèn)題。volitail關(guān)鍵字同樣保證了這種可見(jiàn)性。
?
在這里,我們將探討synchronized使用時(shí)的三種情況:
1. 在對(duì)象上使用synchronized
2. 在普通成員方法上使用synchronized
3. 在靜態(tài)成員方法上使用synchronized
這三種線程同步的表現(xiàn)有何不同?
下面通過(guò)三段示例代碼來(lái)演示這三種情況。這里模擬線程報(bào)數(shù)的場(chǎng)景。
情況一:在普通成員函數(shù)上使用synchronized
public class MyThread extends Thread {public static void main(String[] args) throws Exception {for (int i = 1; i < 100; i++) {MyThread t = new MyThread();t.setName("Thread="+i);t.start();Thread.sleep(100);}}@Overridepublic synchronized void run() {for (int i = 1; i < 10000; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}} }?
對(duì)一個(gè)成員函數(shù)使用synchronized進(jìn)行加鎖,所獲取的鎖,是方法所在對(duì)象本身的對(duì)象鎖。在這里,每個(gè)線程都以自身的對(duì)象作為對(duì)象鎖,要對(duì)線程進(jìn)行同步,要求鎖對(duì)象必須唯一,所以這里多個(gè)線程間同步失敗。
情況二:在對(duì)象上使用synchronized
這里在類中增加一個(gè)成員變量lock,在該變量上使用synchronized:
?
public class MyThread1 extends Thread {private String lock;public MyThread1(String lock) {this.lock = lock;}public static void main(String[] args) throws Exception {String lock = new String("lock");for (int i = 1; i < 100; i++) {Thread t = new MyThread1(lock);t.setName("Thread=" + i);t.start();Thread.sleep(100);}}@Overridepublic void run() {synchronized (lock) {for (int i = 1; i < 10000; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}} }?
100個(gè)線程在創(chuàng)建的時(shí)候,都傳遞了同一個(gè)lock對(duì)象(在main中創(chuàng)建的)去初始化線程類成員lock,因此,這100個(gè)線程都在同一個(gè)lock對(duì)象上進(jìn)行synchronized同步。因此線程同步成功。
情況三:在靜態(tài)成員函數(shù)上使用synchronized
public class MyThread2 extends Thread {public static void main(String[] args) throws Exception {for (int i = 1; i < 10; i++) {Thread t = new MyThread2();t.setName("Thread=" + i);t.start();Thread.sleep(10);}}public static synchronized void func() {for (int i = 1; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}@Overridepublic void run() {func();} }這種情況下,線程獲得的鎖是對(duì)象鎖,而對(duì)象鎖是唯一的,因此多個(gè)進(jìn)程間也能同步成功。
補(bǔ)充:
1. 慎用字符串常量做同步對(duì)象,因?yàn)镴VM內(nèi)部會(huì)把常量字符串轉(zhuǎn)換成同一個(gè)對(duì)象,同理的,基本數(shù)據(jù)除了Float和Double外,也有緩存對(duì)象[-128,127].
2. synchronized方法繼承問(wèn)題:1. 子類會(huì)繼承父類的synchronized方法。2. 如果子類重寫了父類的synchronized方法,必須也加上synchronized關(guān)鍵字,否則子類中的方法將變成非同步的。 3. 同一個(gè)子類對(duì)象中,子類的synchronized方法父類的synchronized方法使用的是同一個(gè)臨界區(qū)。?
(完)
轉(zhuǎn)載于:https://www.cnblogs.com/QG-whz/p/8342276.html
總結(jié)
以上是生活随笔為你收集整理的Java锁机制(一)synchronized的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Cmd使用方式--命令行运行程序
- 下一篇: Java - 枚举