synchronized關鍵字的做用是線程同步,而線程的同步是爲了防止多個線程訪問一個數據對象時,對數據形成的破壞。java
synchronized public void getValue() { ... }
上面的代碼修飾的synchronized是非靜態方法,若是修飾的是靜態方法(static)含義是徹底不同的。具體不同在哪裏,後面會詳細說清楚。 ide
synchronized static public void getValue() { ... }
public void synchronizedMethod() { try { synchronized (this) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } }
上面的代碼塊是synchronized (this)用法,還有synchronized (非this對象)以及synchronized (類.class)這兩種用法,這些使用方式的含義也是有根本的區別的。咱們先帶着這些問題繼續往下看。性能
synchronized關鍵字的使用大體有五種狀況,其中三種是對象鎖,兩種是類鎖:this
下面看一些例子,首先看一下線程不一樣步的狀況:spa
public class SynchronizedTest { public void synchronizedMethod() { try { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } public class MyThread extends Thread { private SynchronizedTest synchronizedTest; public MyThread(SynchronizedTest synchronizedTest) { super(); this.synchronizedTest = synchronizedTest; } @Override public void run() { super.run(); synchronizedTest.synchronizedMethod(); } } public class Main { public static void main(String[] args) throws InterruptedException { SynchronizedTest synchronizedTest1 = new SynchronizedTest(); Thread a = new MyThread(synchronizedTest1); a.setName("a"); a.start(); Thread b = new MyThread(synchronizedTest1); b.setName("b"); b.start(); } }
運行結果:線程
Thread[a,5,main]begin at:2017-09-13 16:52:54 Thread[b,5,main]begin at:2017-09-13 16:52:54 Thread[a,5,main]end at:2017-09-13 16:52:56 Thread[b,5,main]end at:2017-09-13 16:52:56
能夠看到兩個線程交叉執行,要讓這兩個線程依次執行,則須要使用對象鎖同步,能夠將SynchronizedTest類修改爲下面的三種方式來添加對象鎖:code
public class SynchronizedTest { synchronized public void synchronizedMethod() { try { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } 或者 public class SynchronizedTest { public void synchronizedMethod() { try { synchronized (this) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } } 或者 public class SynchronizedTest { Object object = new Object(); public void synchronizedMethod() { try { synchronized (object) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } }
運行結果:orm
Thread[a,5,main]begin at:2017-09-13 16:59:12 Thread[a,5,main]end at:2017-09-13 16:59:14 Thread[b,5,main]begin at:2017-09-13 16:59:14 Thread[b,5,main]end at:2017-09-13 16:59:16
從上面能夠看出,synchronized代碼塊(後兩種方式)使用起來比synchronized方法(第一種方式)要靈活得多。由於也許一個方法中只有一部分代碼只須要同步,若是此時對整個方法用synchronized進行同步,會影響程序執行效率。而使用synchronized代碼塊就能夠避免這個問題,synchronized代碼塊能夠實現只對須要同步的地方進行同步。 對象
若是將Main類修改爲下面這樣,則對象鎖失效:blog
public class Main { public static void main(String[] args) throws InterruptedException { SynchronizedTest synchronizedTest1 = new SynchronizedTest(); SynchronizedTest synchronizedTest2 = new SynchronizedTest(); Thread a = new MyThread(synchronizedTest1); a.setName("a"); a.start(); Thread b = new MyThread(synchronizedTest2); b.setName("b"); b.start(); } }
運行結果:
Thread[b,5,main]begin at:2017-09-13 17:03:26 Thread[a,5,main]begin at:2017-09-13 17:03:26 Thread[b,5,main]end at:2017-09-13 17:03:28 Thread[a,5,main]end at:2017-09-13 17:03:28
由於上面兩個線程調用的是兩個對象中的方法,對象鎖是不起做用的,這種狀況下應該使用類鎖,能夠將SynchronizedTest類修改爲下面的兩種方式來添加類鎖:
public class SynchronizedTest { public void synchronizedMethod() { try { synchronized (SynchronizedTest.class) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } } 或者 public class SynchronizedTest { synchronized public static void synchronizedMethod() { try { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
運行結果:
Thread[a,5,main]begin at:2017-09-13 17:07:02 Thread[a,5,main]end at:2017-09-13 17:07:04 Thread[b,5,main]begin at:2017-09-13 17:07:04 Thread[b,5,main]end at:2017-09-13 17:07:06
須要特別說明:
對於同一個類A,線程1爭奪A對象實例的對象鎖,線程2爭奪類A的類鎖,這二者不存在競爭關係。也就說對象鎖和類鎖互不干預。
靜態方法則必定會同步,非靜態方法需在單例模式才生效,可是也不能都用靜態同步方法,總之用得很差可能會給性能帶來極大的影響。另外,有必要說一下的是Spring的bean默認是單例的。