一、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
總結:線程
關鍵字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靜態方法上是給對象上鎖。因此同一個類下使用兩種加鎖方式的方法是能夠進行異步調用的。