java多線程下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 <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則是全部該類的實例公用一個監視快了,也也就是兩個的區別了

相關文章
相關標籤/搜索