1、介紹
Java語言的關鍵字,可用來給對象和方法或者代碼塊加鎖,當它鎖定一個方法或者一個代碼塊的時候,同一時刻最多只有一個線程執行這段代碼。當兩個並發線程訪問同一個對象object中的這個加鎖同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以后才能執行該代碼塊。然而,當一個線程訪問object的一個加鎖代碼塊時,另一個線程仍可以訪問該object中的非加鎖代碼塊。
2、修飾對象
2.1、修飾this,當前對象,這里的this指的是執行這段代碼的對象,synchronized得到的鎖就是this這個對象的鎖。
線程thread1 訪問對象testSy1的帶有同步代碼塊的add()時,其他線程可以訪問該對象的add()方法嗎?
public class TestSy1 implements Runnable{ private int number; TestSy1(){ number = 0; } public void add(){ synchronized (this){ for(int i=0;i<4;i++){ try{ System.out.println(Thread.currentThread().getName()+":thread:"+(number++)); Thread.sleep(500); }catch (Exception e){ System.out.println("異常"); } } } System.out.println("add"); } public void show(){ for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + " 非同步:" + number); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("show"); }
@Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ add(); }else{ add(); } } public static void main(String[] args){ TestSy1 testSy1 = new TestSy1(); Thread thread1 = new Thread(testSy1,"thread1"); Thread thread2 = new Thread(testSy1,"thread2"); thread1.start(); thread2.start(); } }
結果如下:
thread1:thread:0 thread1:thread:1 thread1:thread:2 thread1:thread:3 thread2:thread:4 thread2:thread:5 thread2:thread:6 thread2:thread:7 可見,其他線程不能訪問add()方法。
修改一下run()
@Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ add(); }else{ show(); } }
結果如下:
thread1:thread:0 thread2 非同步:1 thread1:thread:1 thread2 非同步:2 thread2 非同步:2 thread1:thread:2 thread1:thread:3 thread2 非同步:4 thread2 非同步:4 其他線程可以訪問該對象的show()方法。
那其他線程可以訪問,其他對象的同步方法嗎?
public class TestSy1 implements Runnable{ private int number; TestSy1(){ number = 0; } public void add(){ synchronized (this){ for(int i=0;i<4;i++){ try{ System.out.println(Thread.currentThread().getName()+":thread:"+(number++)); Thread.sleep(500); }catch (Exception e){ System.out.println("異常"); } } } System.out.println("add"); } public void show(){ for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + " 非同步:" + number); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("show"); } @Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ add(); }else{ add(); } } public static void main(String[] args){ TestSy1 testSy1 = new TestSy1(); TestSy1 testSy2 = new TestSy1(); Thread thread1 = new Thread(testSy1,"thread1"); Thread thread2 = new Thread(testSy2,"thread2"); thread1.start(); thread2.start(); } }
結果如下:
thread1:thread:0 thread2:thread:0 thread1:thread:1 thread2:thread:1 thread1:thread:2 thread2:thread:2 thread1:thread:3 thread2:thread:3
總結:修飾this和修飾非靜態方法一樣。線程A訪問對象A的帶有同步代碼塊的方法A時,其他線程可以訪問該對象的非同步方法和其他對象的所有方法。
3、修飾方法
3.1、修飾非靜態方法
public class TestMethod implements Runnable{ private static int number; TestMethod(){ number = 0; }
public synchronized void m1(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public void m2(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } }
@Override
public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ m1(); }else{ m1(); } }
public static void main(String[] args){ TestMethod testMethod = new TestMethod(); Thread thread1 = new Thread(testMethod,"thread1"); Thread thread2 = new Thread(testMethod,"thread2"); thread1.start(); thread2.start(); } }
上述代碼中,synchronized 修飾了非靜態方法m1(),然后生成兩個線程分別訪問對象testMethod的m1()方法,結果如下:
thread1:0 thread1:1 thread1:2 thread2:3 thread2:4 thread2:5 可見,synchronized修飾非靜態方法時,線程thread1訪問testMethod對象的同步方法m1()時,其他線程不能訪問testMethod對象的同步方法m1(),那它可以訪問testMethod的非同步方法m2()嗎?
對上述代碼的run()方法進行修改
@Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ m1(); }else{ m2();//讓thread2訪問非同步方法m2()
} }
結果如下:
thread1:0 thread2:1 thread1:2 thread2:3 thread1:4 thread2:5 可見,線程thread2可以訪問對象testMethod的非同步方法m2(),那線程thread2又可以訪問其他對象的同步和非同步方法嗎?
package p02; public class TestMethod implements Runnable{ private static int number; TestMethod(){ number = 0; } @Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ m1(); }else{ m1(); } } public synchronized void m1(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public void m2(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public static void main(String[] args){ TestMethod testMethod = new TestMethod(); TestMethod testMethod1 = new TestMethod(); Thread thread1 = new Thread(testMethod,"thread1"); Thread thread2 = new Thread(testMethod1,"thread2"); thread1.start(); thread2.start(); } }
結果如下:
thread1:0 thread2:1 thread1:2 thread2:3 thread2:4 thread1:5 可見,其他線程可以訪問對象testMethod1的同步方法。
總結:
修飾非靜態方法時,線程A訪問對象A的非靜態同步方法A時,其他線程可以訪問該對象的非同步方法以及其他對象的任何方法
3.2、修飾靜態方法
因為靜態方法是屬於類的,不是對象的,所以就不測試其他對象了,感興趣的話可以自己試一試。
public class TestMethod implements Runnable{ private static int number; TestMethod(){ number = 0; } @Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ m1(); }else{ m1(); } } public static synchronized void m1(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public void m2(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public static void main(String[] args){ TestMethod testMethod = new TestMethod(); Thread thread1 = new Thread(testMethod,"thread1"); Thread thread2 = new Thread(testMethod,"thread2"); thread1.start(); thread2.start(); } }
上述代碼,m1()是靜態同步方法,當兩個線程同時訪問該方法時會發生什么?
thread1:0 thread1:1 thread1:2 thread2:3 thread2:4 thread2:5 當線程thread1訪問靜態同步方法m1()時,其他線程不能訪問m1(),可以訪問m2(),結果如下:
thread1:0
thread2:1
thread1:2
thread2:3
thread1:4
thread2:5
如果m2()是非靜態同步方法呢?
public class TestMethod implements Runnable{ private static int number; TestMethod(){ number = 0; } @Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ m1(); }else{ m2(); } } public static synchronized void m1(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public synchronized void m2(){ for(int i=0;i<3;i++){ try { System.out.println(Thread.currentThread().getName()+":"+(number++)); Thread.sleep(200); }catch (Exception e){ System.out.println("異常"); } } } public static void main(String[] args){ TestMethod testMethod = new TestMethod(); Thread thread1 = new Thread(testMethod,"thread1"); Thread thread2 = new Thread(testMethod,"thread2"); thread1.start(); thread2.start(); } }
上述代碼中,線程thread1訪問靜態同步方法m1(),線程thread2 訪問非靜態同步方法m2(),結果如下:
thread1:0 thread2:1 thread1:2 thread2:3 thread2:4 thread1:5 線程thread1訪問靜態同步方法m1()時,線程thread2可以訪問非靜態同步方法m2()
總結:修飾靜態方法時,作用於類,同時作用於該類的所有對象。所以,線程A訪問靜態同步方法時,其他線程可以訪問非靜態同步方法和非同步方法,不可以訪問靜態同步方法。
4、修飾類
synchronized不可以直接修飾類,但是可以通過synchronized(類名.class)作用於類。修飾類和修飾靜態方法一樣,也是作用於該類的所有對象。
例子1
public class TestSy1 implements Runnable{ private int number; TestSy1(){ number = 0; } public void add(){ synchronized (TestSy1.class){ for(int i=0;i<4;i++){ try{ System.out.println(Thread.currentThread().getName()+":thread:"+(number++)); Thread.sleep(500); }catch (Exception e){ System.out.println("異常"); } } } System.out.println("add"); } public void show(){ for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + " 非同步:" + number); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("show"); } @Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ add(); }else{ add(); } } public static void main(String[] args){ TestSy1 testSy1 = new TestSy1(); Thread thread1 = new Thread(testSy1,"thread1"); Thread thread2 = new Thread(testSy1,"thread2"); thread1.start(); thread2.start(); } }
結果如下:
thread1:thread:0 thread1:thread:1 thread1:thread:2 thread1:thread:3 thread2:thread:4 thread2:thread:5 thread2:thread:6 thread2:thread:7 線程A訪問帶有synchronized(類名.class)的代碼的方法時,其他線程不能方法該方法。
例子2
線程A訪問帶有synchronized (TestSy1.class)的add()方法,其他線程訪問非靜態同步方法。
public class TestSy1 implements Runnable{ private int number; TestSy1(){ number = 0; } public void add(){ synchronized (TestSy1.class){ for(int i=0;i<4;i++){ try{ System.out.println(Thread.currentThread().getName()+":thread:"+(number++)); Thread.sleep(500); }catch (Exception e){ System.out.println("異常"); } } } System.out.println("add"); } public synchronized void show(){ for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + " 非同步:" + number); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("show"); } @Override public void run() { String name = Thread.currentThread().getName(); if(name.equalsIgnoreCase("thread1")){ add(); }else{ show(); } } public static void main(String[] args){ TestSy1 testSy1 = new TestSy1(); TestSy1 testSy2 = new TestSy1(); Thread thread1 = new Thread(testSy1,"thread1"); Thread thread2 = new Thread(testSy1,"thread2"); thread1.start(); thread2.start(); } }
結果如下:
thread1:thread:0 thread2 非同步:1 thread1:thread:1 thread2 非同步:2 thread1:thread:2 thread2 非同步:3 thread1:thread:3 thread2 非同步:4 thread2 非同步:4 其他線程可以訪問非靜態同步方法,非同步方法更不要說了。
總結:
如果一個方法內帶有synchronized (類名.class)這樣的代碼塊,這個方法就相當於靜態同步方法。
線程A方法該類的靜態同步方法時,其他線程不可以訪問靜態同步方法,但是可以訪問非靜態同步方法和非同步方法。
