Java Synchronized

一、synchronized 同步方法:java

  是對當前對象加鎖。異步

package com.test;

public class TestObject {
    
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName=" + Thread.currentThread().getName() + " beigin time = " + System.currentTimeMillis());
            Thread.sleep(1000);
            System.out.println("end methodA endTime=" + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void methodB() {
        try {
            System.out.println("begin methodB threadName=" + Thread.currentThread().getName() + " beigin time = " + System.currentTimeMillis());
            Thread.sleep(1000);
            System.out.println("end methodB endTime=" + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.test;

public class Run {
    
    public static void main(String[] args) {
        TestObject object = new TestObject();
        
        Thread a = new Thread(new Runnable() {
            
            @Override
            public void run() {
                object.methodA();
            }
        });
        
        Thread b = new Thread(new Runnable() {
            
            @Override
            public void run() {
                object.methodB();
            }
        });
        
        a.start();
        b.start();
    }
}
運行結果:
begin methodA threadName=Thread-0 beigin time = 1527756573018
begin methodB threadName=Thread-1 beigin time = 1527756573018
end methodB endTime=1527756574018
end methodA endTime=1527756574018

經過上面的代碼能夠得知,雖然線程A先持有了object對象的鎖,可是線程B徹底能夠異步調用非synchronized類型的方法。ide

若是將TestObject.java 中的methodB()方法前加上synchronized關鍵字。this

#methodB()前加synchronized關鍵字運行結果:
begin methodA threadName=Thread-0 beigin time = 1527756647320
end methodA endTime=1527756648321
begin methodB threadName=Thread-1 beigin time = 1527756648321
end methodB endTime=1527756649321

結論:spa

  1. A線程先持有object對象的Lock鎖,B線程能夠以異步的方式調用object對象中非synchronized類型的方法。
  2. A線程先持有object對象的Lock鎖,B線程若是在這時調用object對象中的synchronized類型的方法則需等待,也就是同步。

總結:線程

  關鍵字synchronized 擁有鎖重入的功能,也就是在使用synchronized時,但一個線程獲得一個對象鎖後,再次請求此對象鎖時是能夠再次獲得該對象的鎖的。code

  同步不能夠繼承。對象

二、synchronized同步語句塊:blog

  是對某一個對象進行加鎖。synchronized(this) 鎖定的也是當前對象。繼承

  用關鍵字synchronized聲明方法在某些狀況下是有弊端的,好比A線程調用同步方法執行一個長時間的任務,那麼B線程則必須等待比較長的時間。在這樣的狀況下可使用synchronized同步語句塊來解決。

package com.test;


public class Task {
    
    public void methodA() {
        try {
            synchronized (this) {
                System.out.println("A begin time = " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("A end time = " + System.currentTimeMillis());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void methodB() {
        try {
            synchronized (this) {
                System.out.println("B begin time = " + System.currentTimeMillis());
                System.out.println("B end time = " + System.currentTimeMillis());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.test;

public class Run {
    
    public static void main(String[] args) {
        Task task = new Task();
        
        Thread a = new Thread(new Runnable() {
            
            @Override
            public void run() {
                task.methodA();
            }
        });
        
        Thread b = new Thread(new Runnable() {
            
            @Override
            public void run() {
                task.methodB();
            }
        });
        
        a.start();
        b.start();
    }
}
運行結果:
A begin time = 1527757467600
A end time = 1527757469601
B begin time = 1527757469601
B end time = 1527757469601

在使用同步synchronized(this)代碼塊時須要注意的是,當一個線程訪問object一個synchronized(this)同步代碼塊時,其餘線程對同一個object中全部其餘synchronized(this)同步代碼塊的訪問將被阻塞,這說明synchronized使用的 對象監視器 是一個。

 將任意對象做爲對象監視器:

  多個線程調用同一個對象中的不一樣名稱的synchronized同步方法或synchronized(this)同步代碼塊時,調用的效果就是按順序執行,也就是同步的阻塞的。

package com.test;

public class Service {
    private String anyString = new String();
    
    public void methodA() {
        try {
            synchronized (anyString) {
                System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入同步塊");
                Thread.sleep(3000);
                System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開同步塊");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.test;

public class Run {
    
    public static void main(String[] args) {
        Service service = new Service();
        
        Thread a = new Thread(new Runnable() {
            
            @Override
            public void run() {
                service.methodA();
            }
        });
        
        Thread b = new Thread(new Runnable() {
            
            @Override
            public void run() {
                service.methodA();
            }
        });
        
        a.start();
        b.start();
    }
}
運行結果:
線程名爲:Thread-1在1527758321295進入同步塊
線程名爲:Thread-1在1527758324295離開同步塊
線程名爲:Thread-0在1527758324295進入同步塊
線程名爲:Thread-0在1527758327295離開同步塊

使用 synchronized(非this對象x)同步代碼塊進行同步操做時,對象監視器必須是同一個對象。若是不是同一個對象,運行的結果就是異步了。

把Service.java修改成以下:

package com.test;

public class Service {
    private String anyString = new String();
    
    public void methodA() {
        try {
            synchronized (anyString) {
                System.out.println("A begin");
                Thread.sleep(3000);
                System.out.println("A end");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    synchronized public void methodB() {
        System.out.println("B begin");
        System.out.println("B end");
    }
}
運行結果:
B begin
A begin
B end
A end

因爲對象的監視器不一樣,因此運行結果就是異步的。

三、靜態同步synchronized方法與synchronized(class)代碼塊:

關鍵字synchronized還能夠應用在static靜態方法上,若是這樣寫,那就是對當前的*.java文件對應的class類進行加鎖。

package com.test;

public class Service {
    synchronized public static void methodA() {
        
        try {
            System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入同步塊");
            Thread.sleep(3000);
            System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開同步塊");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    synchronized public static void methodB() {
        
        try {
            System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入同步塊");
            Thread.sleep(3000);
            System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開同步塊");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
運行結果:
線程名爲:Thread-1在1527758944286進入同步塊
線程名爲:Thread-1在1527758947287離開同步塊
線程名爲:Thread-0在1527758947287進入同步塊
線程名爲:Thread-0在1527758950287離開同步塊

synchronized關鍵字加到static靜態方法上是給Class上鎖,而synchronized關鍵字加到非static靜態方法上是給對象上鎖。因此同一個類下使用兩種加鎖方式的方法是能夠進行異步調用的。

相關文章
相關標籤/搜索