程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> java多線程(三)——鎖機制synchronized(同步語句塊),多線程synchronized

java多線程(三)——鎖機制synchronized(同步語句塊),多線程synchronized

編輯:JAVA綜合教程

java多線程(三)——鎖機制synchronized(同步語句塊),多線程synchronized


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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved