多執行緒下synchronized修飾static方法與非static方法的區別
一直對多執行緒的概念比較模糊,今天就寫了個關於變數原子操作的小程式,好讓自己加深一下理解
程式碼如下:
-
package atomic;
-
public class JoinThread extends Thread {
-
public static int i = 0;
-
//public static AtomicInteger atomicInteger = new AtomicInteger(0);
-
public synchronized void inc(){
-
i ++;
-
}
-
@Override
-
public void run() {
-
for (int x = 0; x < 10; x++) {
-
inc();
-
try {
-
Thread.sleep(33);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
public static void main(String[] args) throws InterruptedException {
-
// TODO Auto-generated method stub
-
// JoinThread jt = new JoinThread();
-
Thread[] t = new Thread[100];
-
for (int i = 0; i < t.length; i++) {
-
t[i] = new JoinThread();
-
}
-
for (int i = 0; i < t.length; i++) {
-
t[i].start();
-
}
-
for (int i = 0; i < t.length; i++) {
-
t[i].join();
-
}
-
System.out.println(JoinThread.i);
-
}
-
}
執行完發現,i並沒有如想像中的輸出1000,即使i新增volatile進行修飾,也不會輸出1000,值是隨機變化的。
將inc()方法新增static修飾,結果無問題,準確無誤的輸出1000。
另外一種改法,將程式碼改成:
-
Thread[] t = new Thread[100];
-
for (int i = 0; i < t.length; i++) {
-
t[i] = new JoinThread();
-
}
修改成:
-
JoinThread jt = new JoinThread();
-
Thread[] t = new Thread[100];
-
for (int i = 0; i < t.length; i++) {
-
t[i] = new Thread(jt);
-
}
結果無問題,準確無誤的輸出1000
這裡主要涉及到類物件(static方法),物件方法(非static方法)
我們知道,當synchronized修飾一個static方法時,多執行緒下,獲取的是類鎖(即Class本身,注意:不是例項);
當synchronized修飾一個非static方法時,多執行緒下,獲取的是物件鎖(即類的例項物件)
所以,當synchronized修飾一個static方法時,建立執行緒不管是new JoinThread()還是new Thread(new JoinThread()),在run方法中執行inc()方法都是同步的;
相反,當synchronized修飾一個非static方法時,如果用new JoinThread()還是new Thread(new JoinThread())方式建立執行緒,就無法保證同步操作,因為這時
inc()是屬於物件方法,每個執行緒都執有一個獨立的物件例項new JoinThread(),所以多執行緒下執行inc()方法並不會產生互斥,也不會有同步操作。
另外如果考慮到變更的原子操作,可使用atomic包下面的包裝物件,這些物件都是對volatile修飾變數的一種延伸,可保證變數的原子操作而不用去同步方法或
程式碼塊是否同步。