用關鍵字synchronized聲明方法在某些情況下是有弊端的,比如A線程調用同步方法之行一個長時間的任務,那麼B線程必須等待比較長的時間,在這樣的情況下可以使用synchronized同步語句快來解決。
一、用同步代碼塊解決同步方法的弊端
Task類
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Task { 4 5 private String getData1; 6 private String getData2; 7 8 public void doLongTimeTask() { 9 try { 10 System.out.println("begin task"); 11 Thread.sleep(3000); 12 13 String privateGetData1 = "長時間處理任務後從遠程返回的值1 threadName=" 14 + Thread.currentThread().getName(); 15 String privateGetData2 = "長時間處理任務後從遠程返回的值2 threadName=" 16 + Thread.currentThread().getName(); 17 18 synchronized (this) { 19 getData1 = privateGetData1; 20 getData2 = privateGetData2; 21 } 22 23 System.out.println(getData1); 24 System.out.println(getData2); 25 System.out.println("end task"); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } 29 } 30 }
常量工具類
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class CommonUtils { 4 5 public static long beginTime1; 6 public static long endTime1; 7 8 public static long beginTime2; 9 public static long endTime2; 10 }
線程類——2個
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class MyThread1 extends Thread { 4 5 private Task task; 6 7 public MyThread1(Task task) { 8 super(); 9 this.task = task; 10 } 11 12 @Override 13 public void run() { 14 super.run(); 15 CommonUtils.beginTime1 = System.currentTimeMillis(); 16 task.doLongTimeTask(); 17 CommonUtils.endTime1 = System.currentTimeMillis(); 18 } 19 20 }
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class MyThread2 extends Thread { 4 5 private Task task; 6 7 public MyThread2(Task task) { 8 super(); 9 this.task = task; 10 } 11 12 @Override 13 public void run() { 14 super.run(); 15 CommonUtils.beginTime2 = System.currentTimeMillis(); 16 task.doLongTimeTask(); 17 CommonUtils.endTime2 = System.currentTimeMillis(); 18 } 19 20 }
運行類
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Run { 4 5 public static void main(String[] args) { 6 Task task = new Task(); 7 8 MyThread1 thread1 = new MyThread1(task); 9 thread1.start(); 10 11 MyThread2 thread2 = new MyThread2(task); 12 thread2.start(); 13 14 try { 15 Thread.sleep(10000); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 20 long beginTime = CommonUtils.beginTime1; 21 if (CommonUtils.beginTime2 < CommonUtils.beginTime1) { 22 beginTime = CommonUtils.beginTime2; 23 } 24 25 long endTime = CommonUtils.endTime1; 26 if (CommonUtils.endTime2 > CommonUtils.endTime1) { 27 endTime = CommonUtils.endTime2; 28 } 29 30 System.out.println("耗時" + ((endTime - beginTime) / 1000) + " 秒"); 31 } 32 }
結果
1 begin task 2 begin task 3 長時間處理任務後從遠程返回的值1 threadName=Thread-1 4 長時間處理任務後從遠程返回的值1 threadName=Thread-0 5 長時間處理任務後從遠程返回的值2 threadName=Thread-0 6 長時間處理任務後從遠程返回的值2 threadName=Thread-0 7 end task 8 end task 9 耗時3 秒
這裡是用的synchronized代碼鎖,如果換成方法鎖
所有代碼不變,僅更改Task類
1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Task { 4 5 private String getData1; 6 private String getData2; 7 8 public synchronized void doLongTimeTask() { 9 try { 10 System.out.println("begin task"); 11 Thread.sleep(3000); 12 getData1 = "長時間處理任務後從遠程返回的值1 threadName=" 13 + Thread.currentThread().getName(); 14 getData2 = "長時間處理任務後從遠程返回的值2 threadName=" 15 + Thread.currentThread().getName(); 16 System.out.println(getData1); 17 System.out.println(getData2); 18 System.out.println("end task"); 19 } catch (InterruptedException e) { 20 // TODO Auto-generated catch block 21 e.printStackTrace(); 22 } 23 } 24 }
運行結果
1 begin task 2 長時間處理任務後從遠程返回的值1 threadName=Thread-0 3 長時間處理任務後從遠程返回的值2 threadName=Thread-0 4 end task 5 begin task 6 長時間處理任務後從遠程返回的值1 threadName=Thread-1 7 長時間處理任務後從遠程返回的值2 threadName=Thread-1 8 end task 9 耗時6 秒
可以得出結論,當一個線程訪問object的synchronized同步代碼塊時,另一個線程依然可以訪問非同步代碼塊,這樣同步代碼塊就會比同步方法所花費更短的時間,可以得到更高的效率,在同步代碼塊中代碼是同步的,不在同步代碼塊中代碼是異步的。
二、synchronized代碼塊間的同步性
當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對同一個object中所有其他synchronized(this)同步代碼塊的訪問將被阻塞,因為synchronized使用的是一個“對象監視器”
ObjectService類
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class ObjectService { 4 5 public void serviceMethodA() { 6 try { 7 synchronized (this) { 8 System.out 9 .println("A begin time=" + System.currentTimeMillis()); 10 Thread.sleep(2000); 11 System.out 12 .println("A end end=" + System.currentTimeMillis()); 13 } 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 } 18 19 public void serviceMethodB() { 20 synchronized (this) { 21 System.out.println("B begin time=" + System.currentTimeMillis()); 22 System.out.println("B end end=" + System.currentTimeMillis()); 23 } 24 } 25 }
ThreadA
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class ThreadA extends Thread { 4 5 private ObjectService service; 6 7 public ThreadA(ObjectService service) { 8 super(); 9 this.service = service; 10 } 11 12 @Override 13 public void run() { 14 super.run(); 15 service.serviceMethodA(); 16 } 17 18 }
ThreadB
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class ThreadB extends Thread { 4 private ObjectService service; 5 6 public ThreadB(ObjectService service) { 7 super(); 8 this.service = service; 9 } 10 11 @Override 12 public void run() { 13 super.run(); 14 service.serviceMethodB(); 15 } 16 }
Run
1 package com.weishiyao.learn.day4.testSynchorized.ep3; 2 3 public class Run { 4 5 public static void main(String[] args) { 6 ObjectService service = new ObjectService(); 7 8 ThreadA a = new ThreadA(service); 9 a.setName("a"); 10 a.start(); 11 12 ThreadB b = new ThreadB(service); 13 b.setName("b"); 14 b.start(); 15 } 16 17 }
結果
1 A begin time=1459077186249 2 A end end=1459077188249 3 B begin time=1459077188249 4 B end end=1459077188249
三、靜態同步synchronized方法與synchronized(class)代碼塊
關鍵字synchronized還可以用在static靜態方法上,如果這樣寫,那是對當前的java對應的Class類進行上鎖
Service類
1 package com.weishiyao.learn.day4.staticSynchorized; 2 3 public class Service { 4 synchronized public static void printA() { 5 try { 6 System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入A"); 7 Thread.sleep(3000); 8 System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開A"); 9 } catch (Exception e) { 10 e.printStackTrace(); 11 } 12 } 13 14 synchronized public static void printB() { 15 try { 16 System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入B"); 17 Thread.sleep(3000); 18 System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開B"); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } 23 24 synchronized public void printC() { 25 try { 26 System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入C"); 27 Thread.sleep(3000); 28 System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開C"); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 } 33 }
ThreadA
1 package com.weishiyao.learn.day4.staticSynchorized; 2 3 public class ThreadA extends Thread { 4 private Service service; 5 6 public ThreadA(Service service) { 7 super(); 8 this.service = service; 9 } 10 11 @SuppressWarnings("static-access") 12 @Override 13 public void run() { 14 service.printA(); 15 } 16 }
ThreadB、ThreadC類似ThreadA,不再列出
Run
1 package com.weishiyao.learn.day4.staticSynchorized; 2 3 public class Run { 4 public static void main(String[] args) { 5 Service service = new Service(); 6 ThreadA threadA = new ThreadA(service); 7 threadA.setName("A"); 8 threadA.start(); 9 ThreadB threadB = new ThreadB(service); 10 threadB.setName("B"); 11 threadB.start(); 12 ThreadC threadC = new ThreadC(service); 13 threadC.setName("C"); 14 threadC.start(); 15 } 16 }
結果
1 線程名稱為:A在1459078101483進入A 2 線程名稱為:C在1459078101490進入C 3 線程名稱為:A在1459078104484離開A 4 線程名稱為:B在1459078104484進入B 5 線程名稱為:C在1459078104491離開C 6 線程名稱為:B在1459078107484離開B
分析運行結果,A和B是同步運行,C是異步運行,異步的原因是持有不同的鎖,一個是對象鎖,另外一個是Class鎖。
同步synchronized(class)代碼塊的作用和synchronized static方法的作用一樣。
四、內置類與同步
OutClass
1 package com.weishiyao.learn.day4.syncClass.ep5; 2 3 public class OutClass { 4 static class InnerClass1 { 5 public void method1(InnerClass2 class2) { 6 String threadName = Thread.currentThread().getName(); 7 synchronized (class2) { 8 System.out.println(threadName 9 + " 進入InnerClass1類中的method1方法"); 10 for (int i = 0; i < 10; i++) { 11 System.out.println("i=" + i); 12 try { 13 Thread.sleep(100); 14 } catch (InterruptedException e) { 15 16 } 17 } 18 System.out.println(threadName 19 + " 離開InnerClass1類中的method1方法"); 20 } 21 } 22 23 public synchronized void method2() { 24 String threadName = Thread.currentThread().getName(); 25 System.out.println(threadName + " 進入InnerClass1類中的method2方法"); 26 for (int j = 0; j < 10; j++) { 27 System.out.println("j=" + j); 28 try { 29 Thread.sleep(100); 30 } catch (InterruptedException e) { 31 32 } 33 } 34 System.out.println(threadName + " 離開InnerClass1類中的method2方法"); 35 } 36 } 37 38 static class InnerClass2 { 39 public synchronized void method1() { 40 String threadName = Thread.currentThread().getName(); 41 System.out.println(threadName + " 進入InnerClass2類中的method1方法"); 42 for (int k = 0; k < 10; k++) { 43 System.out.println("k=" + k); 44 try { 45 Thread.sleep(100); 46 } catch (InterruptedException e) { 47 48 } 49 } 50 System.out.println(threadName + " 離開InnerClass2類中的method1方法"); 51 } 52 } 53 }
Run
1 package com.weishiyao.learn.day4.syncClass.ep5; 2 3 import com.weishiyao.learn.day4.syncClass.ep5.OutClass.InnerClass1; 4 import com.weishiyao.learn.day4.syncClass.ep5.OutClass.InnerClass2; 5 6 public class Run { 7 8 public static void main(String[] args) { 9 final InnerClass1 in1 = new InnerClass1(); 10 final InnerClass2 in2 = new InnerClass2(); 11 Thread t1 = new Thread(new Runnable() { 12 public void run() { 13 in1.method1(in2); 14 } 15 }, "T1"); 16 Thread t2 = new Thread(new Runnable() { 17 public void run() { 18 in1.method2(); 19 } 20 }, "T2"); 21 Thread t3 = new Thread(new Runnable() { 22 public void run() { 23 in2.method1(); 24 } 25 }, "T3"); 26 t1.start(); 27 t2.start(); 28 t3.start(); 29 } 30 }
結果
1 T2 進入InnerClass1類中的method2方法 2 T1 進入InnerClass1類中的method1方法 3 i=0 4 j=0 5 i=1 6 j=1 7 j=2 8 i=2 9 i=3 10 j=3 11 j=4 12 i=4 13 i=5 14 j=5 15 j=6 16 i=6 17 i=7 18 j=7 19 i=8 20 j=8 21 i=9 22 j=9 23 T2 離開InnerClass1類中的method2方法 24 T1 離開InnerClass1類中的method1方法 25 T3 進入InnerClass2類中的method1方法 26 k=0 27 k=1 28 k=2 29 k=3 30 k=4 31 k=5 32 k=6 33 k=7 34 k=8 35 k=9 36 T3 離開InnerClass2類中的method1方法
同步代碼塊synchronized(class2)對class2上鎖後,其他線程只能以同步的方式調用class2中的靜態同步方法
五、鎖對象的改變
在將任何數據類型作為同步鎖時,需要注意的是,是否有多個線程同時持有鎖對象,如果同時持有相同的鎖對象,則這些線程之間就是同步的;如果分別獲得鎖對象,這些線程之間就是異步的。
MyService
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class MyService { 4 private String lock = "123"; 5 6 public void testMethod() { 7 try { 8 synchronized (lock) { 9 System.out.println(Thread.currentThread().getName() + " begin " 10 + System.currentTimeMillis()); 11 lock = "456"; 12 Thread.sleep(2000); 13 System.out.println(Thread.currentThread().getName() + " end " 14 + System.currentTimeMillis()); 15 } 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 } 20 21 }
ThreadA
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class ThreadA extends Thread { 4 5 private MyService service; 6 7 public ThreadA(MyService service) { 8 super(); 9 this.service = service; 10 } 11 12 @Override 13 public void run() { 14 service.testMethod(); 15 } 16 }
ThreadB
package com.weishiyao.learn.day4.syncClass.ep6; public class ThreadB extends Thread { private MyService service; public ThreadB(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } }
Run1
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class Run1 { 4 5 public static void main(String[] args) throws InterruptedException { 6 7 MyService service = new MyService(); 8 9 ThreadA a = new ThreadA(service); 10 a.setName("A"); 11 12 ThreadB b = new ThreadB(service); 13 b.setName("B"); 14 15 a.start(); 16 Thread.sleep(50); 17 b.start(); 18 } 19 }
結果
1 A begin 1459080143796 2 B begin 1459080143846 3 A end 1459080145797 4 B end 1459080145846
因為50毫秒之後a取得的是鎖"456"
重新改一下Run類
1 package com.weishiyao.learn.day4.syncClass.ep6; 2 3 public class Run2 { 4 5 public static void main(String[] args) throws InterruptedException { 6 7 MyService service = new MyService(); 8 9 ThreadA a = new ThreadA(service); 10 a.setName("A"); 11 12 ThreadB b = new ThreadB(service); 13 b.setName("B"); 14 15 a.start(); 16 b.start(); 17 } 18 }
結果
1 A begin 1459080318782 2 A end 1459080320782 3 B begin 1459080320782 4 B end 1459080322783
只要對象不變,即使對象的屬性被改變,運行結果還是同步的
Service
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class Service { 4 5 public void serviceMethodA(Userinfo userinfo) { 6 synchronized (userinfo) { 7 try { 8 System.out.println(Thread.currentThread().getName()); 9 userinfo.setUsername("abcabcabc"); 10 Thread.sleep(3000); 11 System.out.println("end! time=" + System.currentTimeMillis()); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 } 17 }
Userinfo
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class Userinfo { 4 private String username; 5 private String password; 6 7 public Userinfo() { 8 super(); 9 } 10 11 public Userinfo(String username, String password) { 12 super(); 13 this.username = username; 14 this.password = password; 15 } 16 17 public String getUsername() { 18 return username; 19 } 20 21 public void setUsername(String username) { 22 this.username = username; 23 } 24 25 public String getPassword() { 26 return password; 27 } 28 29 public void setPassword(String password) { 30 this.password = password; 31 } 32 33 }
ThreadA
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class ThreadA extends Thread { 4 5 private Service service; 6 private Userinfo userinfo; 7 8 public ThreadA(Service service, 9 Userinfo userinfo) { 10 super(); 11 this.service = service; 12 this.userinfo = userinfo; 13 } 14 15 @Override 16 public void run() { 17 service.serviceMethodA(userinfo); 18 } 19 20 }
ThreadB
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class ThreadB extends Thread { 4 5 private Service service; 6 private Userinfo userinfo; 7 8 public ThreadB(Service service, 9 Userinfo userinfo) { 10 super(); 11 this.service = service; 12 this.userinfo = userinfo; 13 } 14 15 @Override 16 public void run() { 17 service.serviceMethodA(userinfo); 18 } 19 20 }
運行類
1 package com.weishiyao.learn.day4.syncClass.ep7; 2 3 public class Run { 4 5 public static void main(String[] args) { 6 7 try { 8 Service service = new Service(); 9 Userinfo userinfo = new Userinfo(); 10 11 ThreadA a = new ThreadA(service, userinfo); 12 a.setName("a"); 13 a.start(); 14 Thread.sleep(50); 15 ThreadB b = new ThreadB(service, userinfo); 16 b.setName("b"); 17 b.start(); 18 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 23 } 24 }
結果
1 a 2 end! time=1459080585999 3 b 4 end! time=1459080589000