java synchronized 關鍵字詳解

Java語言的關鍵字,可用來給對象和方法或者代碼塊加鎖,當它鎖定一個方法或者一個代碼塊的時候,同一時刻最多隻有一個線程執行這段代碼。當兩個併發線程訪問同一個對象object中的這個加鎖同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。然而,當一個線程訪問object的一個加鎖代碼塊時,另外一個線程仍然能夠訪問該object中的非加鎖代碼塊。java

 1、synchronized同步方法併發

1.synchronized 同步方法,爲對象鎖ide


public class ExceptionReleaseLock {    public  synchronized  void testLockException(){        if (Thread.currentThread().getName().equals("A")){
            System.out.println("線程名字爲:"+Thread.currentThread().getName()+" 開始運行時間:"+System.currentTimeMillis());            for(int i=0;i<200;i++){                while(i>20){
                    System.out.println("i="+i);
                    Integer.parseInt("abcd");
                }
            }
        }else{
            System.out.println("B線程運行時間:"+System.currentTimeMillis());
        }
    }
}

2.synchronized 鎖重入測試

當線程請求一個由其它線程持有的對象鎖時,該線程會阻塞,而當線程請求由本身持有的對象鎖時,若是該鎖是重入鎖,請求就會成功,不然阻塞.this

 咱們來看看synchronized,它擁有強制原子性的內置鎖機制,是一個重入鎖,因此在使用synchronized時,當一個線程請求獲得一個對象鎖後再次請求此對象鎖,能夠再次獲得該對象鎖,就是說在一個synchronized方法/塊的內部調用本類的其餘synchronized方法/塊時,是永遠能夠拿到鎖。不然,就會形成死鎖。spa

 


/**
 * Created by Administrator on 2015/12/30 0030.
 *
 * 鎖重入機制 */public class LockSyn {    public synchronized  void a(){
        System.out.println("a()");
        b();
    }    public synchronized  void b(){
        System.out.println("b()");
        c();
    }    public synchronized  void c(){
        System.out.println("c()");
    }
}
public class TestLockSyn {    public static  void main(String[] args){         new LockSyn().a();
    }
}


輸出結果:線程

a()
b()
c()

當存在繼承關係時,子類能夠經過可重入鎖調用父類的同步方法。code


public class Animal {    synchronized  public void eat(){
       System.out.println("Animal eat foods");        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class Cat extends  Animal {   synchronized  public  void eatFish(){
       System.out.println("cat eatFish...");       try {
           Thread.sleep(2000);           this.eat();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }


   }
}


測試:orm


public class TestAnimal {    public static  void main(String[] args){
       Cat cat = new Cat();
        cat.eatFish();
    }

}


 

輸出:對象

cat eatFish...
Animal eat foods

 

咱們再來看看重入鎖是怎麼實現可重入性的,其實現方法是爲每一個鎖關聯一個線程持有者和計數器,當計數器爲0時表示該鎖沒有被任何線程持有,那麼任何線程均可能得到該鎖而調用相應的方法;當某一線程請求成功後,JVM會記下鎖的持有線程,而且將計數器置爲1;此時其它線程請求該鎖,則必須等待;而該持有鎖的線程若是再次請求這個鎖,就能夠再次拿到這個鎖,同時計數器會遞增;當線程退出同步代碼塊時,計數器會遞減,若是計數器爲0,則釋放該鎖。

3. 發生異常,鎖自動釋放


public class ExceptionReleaseLock {    public  synchronized  void testLockException(){        if (Thread.currentThread().getName().equals("A")){
            System.out.println("線程名字爲:"+Thread.currentThread().getName()+" 開始運行時間:"+System.currentTimeMillis());            for(int i=0;i<200;i++){                while(i>20){
                    System.out.println("i="+i);
                    Integer.parseInt("abcd");
                }
            }
        }else{
            System.out.println("B線程運行時間:"+System.currentTimeMillis());
        }
    }
}



public class ExceptionReleaseThread1 extends  Thread {    private ExceptionReleaseLock exceptionReleaseLock;    public ExceptionReleaseThread1(ExceptionReleaseLock lock){        this.exceptionReleaseLock=lock;
    }

    @Override    public void run() {
       exceptionReleaseLock.testLockException();
    }
}


public class ExceptionReleaseThread2 extends  Thread {    private ExceptionReleaseLock exceptionReleaseLock;    public ExceptionReleaseThread2(ExceptionReleaseLock lock){        this.exceptionReleaseLock=lock;
    }

    @Override    public void run() {
       exceptionReleaseLock.testLockException();
    }
}


測試:


/**
 * Created by Administrator on 2015/12/30 0030.
 *
 * 發生異常後,鎖自動釋放 */public class TestExceptionRelease {    public static void main(String[] args){
        ExceptionReleaseLock lock = new ExceptionReleaseLock();
        ExceptionReleaseThread1 a = new ExceptionReleaseThread1(lock);
        ExceptionReleaseThread2 b = new ExceptionReleaseThread2(lock);

        a.setName("A");
        b.setName("B");
        a.start();
        b.start();
    }
}


輸出結果:

A線程發生異常後釋放了鎖,線程B正常執行。

4. 同步不具備繼承性.


public class Cat extends  Animal {

  

    @Override    public  void eat() {

        System.out.println("noSynchronized ....");
    }
}


須要在方法前加上synchronized同步。

2、synchronized 同步語句塊

synchronized 方法的缺陷:若將一個大的方法聲明爲synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明爲

synchronized ,因爲在線程的整個生命期內它一直在運行,所以將致使它對本類任何 synchronized 方法的調用都永遠不會成功。固然咱們可

以經過將訪問類成員變量的代碼放到專門的方法中,將其聲明爲 synchronized ,並在主方法中調用來解決這一問題,可是 Java 爲咱們提供

了更好的解決辦法,那就是 synchronized 塊。  
2. synchronized 塊:經過 synchronized關鍵字來聲明synchronized 塊。語法以下:  
synchronized(syncObject) {  
//容許訪問控制的代碼  
}  
synchronized 塊是這樣一個代碼塊,其中的代碼必須得到對象 syncObject (如前所述,能夠是類實例或類)的鎖方能執行,具體機

制同前所述。因爲能夠針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。  
對synchronized(this)的一些理解 
1、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線

程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。  
2、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized

(this)同步代碼塊。  
3、尤爲關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)

同步代碼塊的訪問將被阻塞。  
4、第三個例子一樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個

object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。  
5、以上規則對其它對象鎖一樣適用

複製代碼

package com.xwolf.java.thread.ch2;/**
 * Created by Administrator on 2016/1/4 0004.
 *
 * synchronized 代碼塊 一樣是對象加鎖 */public class SyncBlock {    public synchronized  void a(){
        System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&");        try {
            Thread.sleep(2000);
            System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }    public   void b(){       try {           synchronized (this){
               System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&");
               Thread.sleep(2000);
               System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$");
           }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

複製代碼

複製代碼

public class SyncThreadA extends Thread {    private SyncBlock block;

    SyncThreadA(SyncBlock block){        this.block=block;
    }

    @Override    public void run() {
        block.a();
    }
}


public class SyncThreadB extends Thread {    private SyncBlock block;

    SyncThreadB(SyncBlock block){        this.block=block;
    }

    @Override    public void run() {
        block.b();
    }
}

複製代碼

複製代碼

public class SyncThreadTest {    public static  void main(String[] args){

        SyncBlock block =new SyncBlock();

        SyncThreadA a = new SyncThreadA(block);
        a.setName("A");

        SyncThreadB b = new SyncThreadB(block);
        b.setName("B");

        a.start();
        b.start();
    }
}

複製代碼

3、static synchronized 和非static synchronized 方法

複製代碼

package com.xwolf.java.thread.ch2;/**
 * Created by Administrator on 2016/1/4 0004.
 *
 * 驗證static方法和非static方法synchronized 區別 */public class StaticSync {    public synchronized  static void a(){
        System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&");        try {
            Thread.sleep(2000);
            System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }    public synchronized  static void b(){
        System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&");        try {
            Thread.sleep(2000);
            System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }    public synchronized   void c(){
        System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&");        try {
            Thread.sleep(2000);
            System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

複製代碼

複製代碼

package com.xwolf.java.thread.ch2;/**
 * Created by Administrator on 2016/1/4 0004. */public class StaticThreadA  extends  Thread{    private StaticSync staticSync;   public  StaticThreadA(StaticSync sync){        this.staticSync=sync;
    }

    @Override    public void run() {
        staticSync.a();
    }
}

複製代碼

測試:

複製代碼

package com.xwolf.java.thread.ch2;/**
 * Created by Administrator on 2016/1/4 0004. */public class StaticSyncTest {    public static void main(String[] args){


        StaticSync sync = new StaticSync();

        StaticThreadA a = new StaticThreadA(sync);
        a.setName("A");

        StaticThreadB b = new StaticThreadB(sync);

        b.setName("B");

        StaticThreadC c = new StaticThreadC(sync);
        c.setName("C");

        a.start();
        b.start();
        c.start();
    }



}

複製代碼

 

當一個synchronized關鍵字修飾的方法同時又被static修飾,以前說過,非靜態的同步方法會將對象上鎖,可是靜態方法不屬於對象,而是屬於類,它會將這個方法所在的類的Class對象上鎖。一個類無論生成多少個對象,它們所對應的是同一個Class對象。

相關文章
相關標籤/搜索