synchronize能夠在多個線程操做同一個成員變量或者方法時,實現同步(或者互斥)的效果。
synchronized能夠做用於方法,以及方法內部的代碼塊。異步
//1 synchronized void method(){} //2 static synchronized void method(){} //3 synchronized void method(){ synchronized(鎖對象){ } } //4 static synchronized void method(){ synchronized(鎖對象){ } }
那麼在上面的示例中,它們分別持有的鎖對象是誰?
synchronized做用於非靜態方法以及非靜態方法內部的代碼塊,持有的是當前類的對象的鎖,而且是同一個鎖。做用於靜態方法及其內部的代碼塊,持有的是當前類的Class對象的鎖,而且和非靜態方法不是同一個鎖。
經過代碼來驗證。ide
public class SynchronizedTest { private synchronized void test1(){ for (int x = 0; x < 5; x++) { System.out.println("test1---"+x); } } private void test2(){ synchronized(this) { for (int x = 0; x < 5; x++) { System.out.println("---test2---"+x); } } } private static synchronized void test3(){ for (int x = 0; x < 5; x++) { System.out.println("------test3---"+x); } } private static void test4(){ synchronized (SynchronizedTest.class){ for (int x = 0; x < 5; x++) { System.out.println("---------test4---"+x); } } } public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test1(); } }).start(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test2(); } }).start(); new Thread(new Runnable() { @Override public void run() { test3(); } }).start(); new Thread(new Runnable() { @Override public void run() { test4(); } }).start(); } }
執行結果this
test1---0 ------test3---0 test1---1 ------test3---1 test1---2 test1---3 ------test3---2 test1---4 ------test3---3 ------test3---4 ---test2---0 ---test2---1 ---test2---2 ---test2---3 ---test2---4 ---------test4---0 ---------test4---1 ---------test4---2 ---------test4---3 ---------test4---4
test1和test2不會交叉執行,test3和test4也不會交叉執行。非靜態方法以及方法內部的代碼塊持有的是同一個對象鎖,它們是同步執行的。靜態方法和內部的代碼塊持有的是當前類的Class對象鎖,它們是同步執行的。而靜態方法和非靜態方法持有的不是同一個鎖,它們是異步的。線程
字符串常量做爲鎖,會有什麼結果?code
final String a = "100"; final String b = "100"; new Thread(new Runnable() { @Override public void run() { synchronized (a){ while (true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } },"thread-a").start(); new Thread(new Runnable() { @Override public void run() { synchronized (b){ while (true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } },"thread-b").start();
這裏字符串a和b雖然是兩個對象,可是聲明b時,會將字符串常量池中已存在的a的值直接賦給b。這樣a和b實際上是同樣的。這樣線程thread-a和thread-b同時搶佔同一個鎖,一旦一個線程搶到該鎖,另外一個線程就再也獲取不到該鎖。對象
本身覆蓋了父類被synchronized修飾的方法,子類方法若是須要同步性,也須要用synchronized修飾。
定義子類Sub繼承自SynchronizedTest繼承
class Sub extends SynchronizedTest{ @Override public void test2() { //super.test2(); for (int x = 0; x < 15; x++) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } }
而後開啓兩個線程調用Sub的test2()方法。字符串
final Sub sub = new Sub(); new Thread(new Runnable() { @Override public void run() { sub.test2(); } },"Sub---A").start(); new Thread(new Runnable() { @Override public void run() { sub.test2(); } },"Sub---B").start();
打印結果get
Sub---A Sub---B Sub---A Sub---B Sub---A Sub---B Sub---A Sub---B
可見Sub的test2()方法並無同步性。也就是synchronized不能被繼承。同步
使用synchronized時,當一個線程請求一個對象鎖時,再次請求該鎖是能夠當即獲得的。
public class SynchronizedTest { private synchronized void test1(){ for (int x = 0; x < 5; x++) { System.out.println("test1---"+x); } test2(); } public void test2(){ synchronized(this) { for (int x = 0; x < 5; x++) { System.out.println("---test2---"+x); } } } public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test1(); } }).start(); } }
在方法test1()中調用方法test2(),並不須要等待鎖,而是當即獲取到鎖。
把子類Sub的test2()方法也改爲synchronized修飾。並在其內部調用父類的test2()方法。能得到鎖嗎?
class Sub extends SynchronizedTest{ @Override public synchronized void test2() { //調用父類的同步方法 super.test2(); for (int x = 0; x < 15; x++) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } }
打印結果
---test2---0 ---test2---1 ---test2---2 ---test2---3 ---test2---4 Sub---A Sub---A Sub---A Sub---A
能夠看到父類方法執行完畢,子類的方法當即執行。可見,在子父類的繼承關係中,也支持可重入鎖這個特性。
synchronized做用於整個方法,可能引發方法執行效率降低。建議將方法內部須要同步的代碼用synchronized修飾,也就是synchronized代碼塊。
在一個類中假若有多個同步方法,它們之間並不須要互斥。那麼使用同一個鎖,會大大下降效率。能夠定義多個同步鎖對象。
Object obj1 = new Object(); Object obj2 = new Object(); public void method1(){ synchronized(obj1){ } } public void method2(){ synchronized(obj2){ } }