想要解決多線程問題,首先我們需要掌握Java鎖的原理。
我們常用鎖Synchronized的方式
一:方法鎖
1:方法鎖鎖的是該對象。例如:
public static void main(String[] arr) {
final Test1 test1 = new Test1();
for (int i = 0; i < 15; i++) {
new Thread(new Runnable() {
@Override
public void run() {
test1.send();
}
}).start();
}
}
public class Test1 {
int data;
public synchronized void send() {
System.out.println();
System.out.println("方法入口******= "+
Thread.currentThread().getName());
for (int i = 0; i < 20000; i++) {
data++;
}
System.out.println(data);
System.out.println("方法出口&&&&&&= "+
Thread.currentThread().getName());
data = 0;
}
}
輸出結果爲:
說明線程處於排隊狀態,該方法只允許一個線程執行,執行完之後釋放鎖,線程重新獲取鎖執行。
2:去掉send方法上的鎖 synchronized關鍵字
public class Test1 {
int data;
public void send() {
for (int i = 0; i < 20000; i++) {
data++;
}
System.out.println(data);
data = 0;
}
}
public class Test1 {
int data;
public void send() {
System.out.println();
System.out.println("方法入口******= "+
Thread.currentThread().getName());
for (int i = 0; i < 20000; i++) {
data++;
}
System.out.println(data);
System.out.println("方法出口&&&&&&= "+
Thread.currentThread().getName());
data = 0;
}
}
輸出結果爲
由此可見,去掉鎖,則其中一個線程在執行該方法時,其他線程依然可以執行。
3:當多個實例調用send方法時
public static void main(String[] arr) {
for (int i = 0; i < 15; i++) {
final Test1 test1 = new Test1();
new Thread(new Runnable() {
@Override
public void run() {
test1.send();
}
}).start();
}
}
public class Test1 {
int data;
public void send() {
System.out.println();
System.out.println("方法入口******= "+
Thread.currentThread().getName());
for (int i = 0; i < 20000; i++) {
data++;
}
System.out.println(data);
System.out.println("方法出口&&&&&&= "+
Thread.currentThread().getName());
data = 0;
}
}
輸出結果爲:
所以由此說明,方法鎖,鎖的是當前實例。當有多個實例,鎖不受影響。
二:代碼塊鎖
該方式的鎖,比較靈活。既可以鎖當前實體,也可以鎖定當前類。鎖定當前實體,則跟方法鎖一樣,這裏不再說明。重點說下鎖定類。
1:鎖定類。當鎖定類的時候,不論是單一實體還是多實體的多線程請求該段上鎖的代碼塊,都將搶鎖,其他線程處於等待。
public static void main(String[] arr) {
for (int i = 0; i < 10; i++) {
final Test1 test1 = new Test1();
new Thread(new Runnable() {
@Override
public void run() {
test1.send();
}
}).start();
}
}
public class Test1 {
int data;
public void send() {
synchronized (Test1.class){
System.out.println();
System.out.println("方法入口******= "+
Thread.currentThread().getName());
for (int i = 0; i < 20000; i++) {
data++;
}
System.out.println(data);
System.out.println("方法出口&&&&&&= "+
Thread.currentThread().getName());
data = 0;
}
}
}
輸出結果爲:
由此說明,鎖定類,即使是多實體的多線程,依然是有效果的。
總結:
1:當一個類中有多個方法鎖時,具有方法鎖的方法則彼此間可以互相調用,因爲此時都屬於同一線程,並且又是同一把鎖,所以不會受影響。
2:當一個線程獲取到該對象的方法鎖時,其他線程無法獲取該對象的方法鎖及該對應對應的代碼塊鎖。
3:代碼塊鎖中的鎖,可以靈活變換,可以鎖定其他實體,也可以鎖定該片段爲類的鎖,使該類的多實體多線程搶用同一鎖,其他線程處於等待狀態。
4:代碼塊鎖在一個線程搶佔資源後,其他線程處於等待狀態,而並不是該線程執行完之後其他線程纔可執行,當一個線程for循環執行一個代碼塊鎖。在執行一次後會釋放鎖,然後共同搶鎖,有可能出現for循環只執行了一次,鎖的資源被其他線程搶走了。所以在處理業務邏輯要特別注意。