您好,登錄后才能下訂單哦!
小編給大家分享一下Synchronized怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變量的內存可見性
Java中每一個對象都可以作為鎖,這是synchronized實現同步的基礎:
1. 普通同步方法,鎖是當前實例對象
2. 靜態同步方法,鎖是當前類的class對象
3. 同步方法塊,鎖是括號里面的對象
synchronized 獲取的鎖,在方法拋出異常的時候會自動解鎖
利用javap工具查看生成的class文件信息來分析Synchronize的實現
從上面可以看出,同步代碼塊是使用monitorenter和monitorexit指令實現的,同步方法(在這看不出來需要看JVM底層實現)依靠的是方法修飾符上的ACC_SYNCHRONIZED實現。
同步代碼塊:monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入到同步代碼塊的結束位置,JVM需要保證每一個monitorenter都有一個monitorexit與之相對應。任何對象都有一個monitor與之相關聯,當且一個monitor被持有之后,他將處于鎖定狀態。線程執行到monitorenter指令時,將會嘗試獲取對象所對應的monitor所有權,即嘗試獲取對象的鎖;
同步方法:synchronized方法則會被翻譯成普通的方法調用和返回指令如:invokevirtual、areturn指令,在VM字節碼層面并沒有任何特別的指令來實現被synchronized修飾的方法,而是在Class文件的方法表中將該方法的access_flags字段中的synchronized標志位置1,表示該方法是同步方法并使用調用該方法的對象或該方法所屬的Class在JVM的內部對象表示Klass做為鎖對象。
同步方法就是在方法前加關鍵字synchronized,然后被同步的方法一次只能有一個線程進入,其他線程等待。
而同步塊則是在方法內部使用大括號使得一個代碼塊得到同步。同步塊會有一個同步的”目標“,使得同步塊更加靈活一些(同步塊可以通過”目標“決定需要鎖定的對象)。一般情況下,如果此”目標“為this,那么同步方法和同步塊沒有太大的區別。
synchronized 加到 static 方法前面是給class 加鎖,即類鎖;而synchronized 加到非靜態方法前面是給對象上鎖。這兩者的區別我用代碼來演示下:
對象鎖和類鎖是不同的鎖,所以多個線程同時執行這2個不同鎖的方法時,是異步的。
在Task2 中定義三個方法 doLongTimeTaskA和doLongTimeTaskB是類鎖,而doLongTimeTaskC是對象鎖。
public class Task2 {
public synchronized static void doLongTimeTaskA() {
System.out.println("name = " + Thread.currentThread().getName() + ", begain");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + Thread.currentThread().getName() + ", end");
}
public synchronized static void doLongTimeTaskB() {
System.out.println("name = " + Thread.currentThread().getName() + ", begain");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + Thread.currentThread().getName() + ", end");
}
public synchronized void doLongTimeTaskC() {
System.out.println("name = " + Thread.currentThread().getName() + ", begain");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + Thread.currentThread().getName() + ", end");
}
三個線程的代碼如下:
class ThreadA extends Thread{
private Task2 mTask2;
public ThreadA(Task2 tk){
mTask2 = tk;
}
public void run() {
mTask2.doLongTimeTaskA();
}
}
class ThreadB extends Thread{
private Task2 mTask2;
public ThreadB(Task2 tk){
mTask2 = tk;
}
public void run() {
mTask2.doLongTimeTaskB();
}
}
class ThreadC extends Thread{
private Task2 mTask2;
public ThreadC(Task2 tk){
mTask2 = tk;
}
public void run() {
mTask2.doLongTimeTaskC();
}
}
main函數中執行代碼如下:
Task2 mTask2 = new Task2();
ThreadA ta = new ThreadA(mTask2);
ThreadB tb = new ThreadB(mTask2);
ThreadC tc = new ThreadC(mTask2);
ta.setName("A");
tb.setName("B");
tc.setName("C");
ta.start();
tb.start();
tc.start();
}
執行的結果如下:
name = A, begain, time = 1487311199783
name = C, begain, time = 1487311199783
name = C, end, time = 1487311200784
name = A, end, time = 1487311200784
name = B, begain, time = 1487311200784
name = B, end, time = 1487311201784
可以看出由于 doLongTimeTaskA和doLongTimeTaskB都是類鎖,即同一個鎖,所以 A和B是按順序執行,即同步的。而C是對象鎖,和A/B不是同一種鎖,所以C和A、B是 異步執行的。(A、B、C代指上面的3中方法)。
我們知道對象鎖要想保持同步執行,那么鎖住的必須是同一個對象。下面就修改下上面的來證明:
Task2.java不變,修改ThreadA 和 ThreadB 如下:
class ThreadA extends Thread{
private Task2 mTask2;
public ThreadA(Task2 tk){
mTask2 = tk;
}
public void run() {
mTask2.doLongTimeTaskC();
}
}
class ThreadB extends Thread{
private Task2 mTask2;
public ThreadB(Task2 tk){
mTask2 = tk;
}
public void run() {
mTask2.doLongTimeTaskC();
}
}
main方法如下:
Task2 mTaska = new Task2();
Task2 mTaskb = new Task2();
ThreadA ta = new ThreadA(mTaska );
ThreadB tb = new ThreadB(mTaskb );
ta.setName("A");
tb.setName("B");
ta.start();
tb.start();
結果如下:
name = A, begain, time = 1487311905775
name = B, begain, time = 1487311905775
name = B, end, time = 1487311906775
name = A, end, time = 1487311906775
從結果看來,對象鎖鎖的對象不一樣,分別是mTaska , mTaskb,所以線程A和線程B調用 doLongTimeTaskC 是異步執行的。
但是,類鎖可以對類的所有對象的實例起作用。只需修改ThradA
和 ThreadB,main 方法不做改變,修改如下:
class ThreadA extends Thread{
private Task2 mTask2;
public ThreadA(Task2 tk){
mTask2 = tk;
}
public void run() {
//mTask2.doLongTimeTaskC();
mTask2.doLongTimeTaskA();
}
}
class ThreadB extends Thread{
private Task2 mTask2;
public ThreadB(Task2 tk){
mTask2 = tk;
}
public void run() {
//mTask2.doLongTimeTaskC();
mTask2.doLongTimeTaskA();
}
}
結果如下:
name = A, begain, time = 1487312239674
name = A, end, time = 1487312240674
name = B, begain, time = 1487312240674
name = B, end, time = 1487312241674
可以看出 在線程A執行完doLongTimeTaskA方法后,線程B才會獲得該類鎖接著去執行doLongTimeTaskA。也就是說,類鎖對所有的該類對象都能起作用。
總結:
1. 如果多線程同時訪問同一類的 類鎖(synchronized 修飾的靜態方法)以及對象鎖(synchronized 修飾的非靜態方法)這兩個方法執行是異步的,原因:類鎖和對象鎖是2中不同的鎖。
2. 類鎖對該類的所有對象都能起作用,而對象鎖不能。
之前談到過,每個鎖關聯一個線程持有者和一個計數器。當計數器為0時表示該鎖沒有被任何線程持有,那么任何線程都都可能獲得該鎖而調用相應方法。當一個線程請求成功后,JVM會記下持有鎖的線程,并將計數器計為1。此時其他線程請求該鎖,則必須等待。而該持有鎖的線程如果再次請求這個鎖,就可以再次拿到這個鎖,同時計數器會遞增。當線程退出一個synchronized方法/塊時,計數器會遞減,如果計數器為0則釋放該鎖。
以上是“Synchronized怎么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。