併發能產生問題的狀況是,兩個線程都去競爭同一個對象纔會產生問題,若是你的靜態方法只是簡單的邏輯是不會有問題的,可是若是你的線程都是去修改靜態變量的值的話,應該是會形成線程問題的
總的結論:java是線程安全的,即對任何方法(包括靜態方法)均可以不考慮線程衝突,但有一個前提,就是不能存在全局變量。若是存在全局變量,則須要使用同步機制。
以下經過一組對比例子從頭講解:
在多線程中使用靜態方法會發生什麼事?也就是說多線程訪問同一個類的static靜態方法會發生什麼事?是否會發生線程安全問題?
public class Test {
public static void operation(){
// ... do something
}
}
事實證實只要在靜態函數中沒有處理多線程共享數據,就不存在着多線程訪問同一個靜態方法會出現資源衝突的問題。下面看一個例子:
public class StaticThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
StaticAction.print();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new StaticThread()).start();
}
}
}
public class StaticAction {
public static int i = 0;
public static void print() {
int sum = 0;
for (int i = 0; i < 10; i++) {
System.out.print("step " + i + " is running.");
sum += i;
}
if (sum != 45) {
System.out.println("Thread error!");
System.exit(0);
}
System.out.println("sum is " + sum);
}
}
實際執行的結果顯示各個線程對靜態方法的訪問是交叉執行的,可是這並不影響各個線程靜態方法print()中sum值的計算。也就是說,在此過程當中沒有使用全局變量的靜態方法在多線程中是安全的,靜態方法是否引發線程安全問題主要看該靜態方法是否對全局變量(靜態變量static member)進行修改操做。
在多線程中使用同一個靜態方法時,每一個線程使用各自的實例字段(instance field)的副本,而共享一個靜態字段(static field)。因此說,若是該靜態方法不去操做一個靜態成員,只在方法內部使用實例字段(instance field),不會引發安全性問題。 可是,若是該靜態方法操做了一個靜態變量,則須要靜態方法中採用互斥訪問的方式進行安全處理。咱們來看一下沒有使用互斥訪問的話會產生怎樣的問題:public class StaticAction { public static int i = 0; public static void incValue() { int temp = StaticAction.i; try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } temp++; StaticAction.i = temp; }}public class StaticThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub StaticAction.incValue(); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new StaticThread()).start(); } try { Thread.sleep(1000); //預留足夠的時間讓上面的線程跑完 } catch (Exception e) { e.printStackTrace(); } System.out.println(StaticAction.i); }} 實際運行結果顯示i值爲隨機的數字。爲了實現互斥訪問,這時咱們須要加入一個synchronized關鍵字。代碼修改以下:public class StaticAction { public static int i = 0; public synchronized static void incValue() { int temp = StaticAction.i; try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } temp++; StaticAction.i = temp; }}public class StaticThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub StaticAction.incValue(); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new StaticThread()).start(); } try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println(StaticAction.i); }} 運行結果則必然是100。 加入synchronized關鍵字的靜態方法稱爲同步靜態方法。 在訪問同步靜態方法時,會獲取該類的「Class」對象,因此當一個線程進入同步的靜態方法中時,線程監視器獲取類自己的對象鎖,其它線程不能進入這個類的任何靜態同步方法。它不像實例方法,由於多個線程能夠同時訪問不一樣實例同步實例方法。這個其實就是操做系統中的用信號量實現進程的互斥與同步問題,若是涉及在同一個類中有多個靜態方法中處理多線程共享數據的話,那就變成用信號量解決生產者-消費者問題。也就是說,靜態方法是一份臨界資源,對靜態方法的訪問屬於進入臨界區;對靜態變量的修改是一份臨界資源,對靜態變量的修改屬於進入臨界區。