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 <span style="font-family: Arial, Helvetica, sans-serif;">JoinThread</span>(); } 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
,值是隨機變化的。
java
將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); }
結果無問題,準確無誤的輸出1000jvm
這裏主要涉及到類對象(static方法),對象方法(非static方法)ide
咱們知道,當synchronized修飾一個static方法時,多線程下,獲取的是類鎖(即Class自己,注意:不是實例);atom
當synchronized修飾一個非static方法時,多線程下,獲取的是對象鎖(即類的實例對象)spa
因此,當synchronized修飾一個static方法時,建立線程無論是new JoinThread()仍是new Thread(new JoinThread()),在run方法中執行inc()方法都是同步的;線程
相反,當synchronized修飾一個非static方法時,若是用new JoinThread()仍是new Thread(new JoinThread())方式建立線程,就沒法保證同步操做,由於這時設計
inc()是屬於對象方法,每一個線程都執有一個獨立的對象實例new JoinThread(),因此多線程下執行inc()方法並不會產生互斥,也不會有同步操做。
另外若是考慮到變動的原子操做,可以使用atomic包下面的包裝對象,這些對象都是對volatile修飾變量的一種延伸,可保證變量的原子操做而不用去同步方法或
代碼塊是否同步。
一個日本做者-結成浩的《java多線程設計模式》有這樣的一個列子:
pulbic class Something(){
public synchronized void isSyncA(){}
public synchronized void isSyncB(){}
public static synchronized void cSyncA(){}
public static synchronized void cSyncB(){}
}
那麼,加入有Something類的兩個實例a與b,那麼下列組方法何以被1個以上線程同時訪問呢
a. x.isSyncA()與x.isSyncB()
b. x.isSyncA()與y.isSyncA()
c. x.cSyncA()與y.cSyncB()
d. x.isSyncA()與Something.cSyncA()
這裏,很清楚的能夠判斷:
a,都是對同一個實例的synchronized域訪問,所以不能被同時訪問
b,是針對不一樣實例的,所以能夠同時被訪問
c,由於是static synchronized,因此不一樣實例之間仍然會被限制,至關於Something.isSyncA()與 Something.isSyncB()了,所以不能被同時訪問。
那麼,第d呢?,書上的 答案是能夠被同時訪問的,答案理由是synchronzied的是實例方法與synchronzied的類方法因爲鎖定(lock)不一樣的緣由。
synchronized是對類的當前實例進行加鎖,防止其餘線程同時訪問該類的該實例的全部synchronized塊,注意這裏是「類的當前實例」, 類的兩個不一樣實例就沒有這種約束了。那麼static synchronized剛好就是要控制類的全部實例的訪問了,static synchronized是限制線程同時訪問jvm中該類的全部實例同時訪問對應的代碼快。實際上,在類中某方法或某代碼塊中有 synchronized,那麼在生成一個該類實例後,改類也就有一個監視快,放置線程併發訪問改實例synchronized保護快,而static synchronized則是全部該類的實例公用一個監視快了,也也就是兩個的區別了