Java synchronized多執行緒互斥技術
阿新 • • 發佈:2019-02-13
在java中,synchronized是用來控制執行緒同步的,既為了讓一段程式碼不允許多個執行緒同時訪問,需要排隊一個一個執行,就像我們生活中排隊在公共電話亭打電話一樣,一個人打好電話出來,另外個人才可以進去打電話
問題1:描述了synchronized物件鎖的問題
問題2:描述了static靜態方法加上synchronized的問題
問題1
程式碼
不要認為加上synchronized就萬事大吉了,看下下面一段程式碼
public class DemoTest01 {
public static void main(String[] args) {
final OuterPuter puter = new OuterPuter();
//第一個執行緒
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
puter.outerPut1("ABCD");
}
}
}).start();
//第二個執行緒
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
puter.outerPut1("我愛程式設計");
}
}
}).start();
}
}
class OuterPuter {
public void outerPut1(String name) {
// 將名字一個字一個字的打印出來
synchronized (name) {//這段程式碼被要求每次只能一個執行緒進行執行
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
}
}
執行結果
從結果可以看出,並不是我們想要的結果,我們要的結果是:每行要麼是ABCD,要麼是我愛程式設計
從同步程式碼塊synchronized可以看出,後面的物件name,在兩次執行緒執行的時候,物件不是同一個物件我(只有在鎖物件為同一個鎖),既不是同一個鎖,下面我們進行優化
優化方案1
class OuterPuter {
public void outerPut1(String name) {
// 將名字一個字一個字的打印出來
synchronized (this) {//這裡所物件改成this,即可
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
}
將鎖物件改成this後,因為我們建立物件是線上程開啟前,確保物件的唯一,但是千萬不能放到兩個執行緒的run方法裡,因為放到run方法裡,物件建立了兩次,鎖物件不是唯一的了
優化方案2
class OuterPuter {
public synchronized void outerPut1(String name) {
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
直接在執行的方法上加上synchronized ,其實他的物件鎖也是this,所以也可以實現兩個執行緒互斥
問題2
程式碼
看下下面一段程式碼,是否能夠實現執行緒互斥
public class DemoTest01 {
public static void main(String[] args) {
final OuterPuter puter = new OuterPuter();
// 第一個執行緒
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
puter.outerPut2("ABCD");
}
}
}).start();
// 第二個執行緒
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
OuterPuter.outerPut3("我愛程式設計");
}
}
}).start();
}
}
class OuterPuter {
public synchronized void outerPut2(String name) {
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
//靜態方法
public static synchronized void outerPut3(String name) {
// 將名字一個字一個字的打印出來
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
}
執行結果:
下面我們分析下原因:
這裡有另外個知識點:
因為outerPut3方法前面加了static修飾符,靜態方法裡是無法使用this的,他的鎖物件不是this,而是類的Class物件,所以我們需要對程式碼進行優化
優化方案:
class OuterPuter {
//非靜態方法
public void outerPut2(String name) {
//把鎖物件也改成類的Class物件
synchronized (OuterPuter.class) {
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
}
//靜態方法
public static synchronized void outerPut3(String name) {
// 將名字一個字一個字的打印出來
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 換行
System.out.println();
}
}
將outerPut2下的程式碼塊也用類的Class物件鎖,這樣可以保證兩個方法體的鎖物件都是同一個,就可以實現了同步程式碼塊的功能,實現了執行緒之間互斥