用關鍵字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