synchronized关键字小结

Posted Kingsley

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了synchronized关键字小结相关的知识,希望对你有一定的参考价值。

1. synchronized同步方法

1) synchronized修饰方法,表示方法是同步的,当某线程进入并拿到当前整个对象的锁时

a. 其他synchronized方法排队等锁

b. 非synchronized方法可异步执行

示例代码(折叠)

 1 package com.khlin.thread;
 2 
 3 public class SynchronizedTest {
 4     
 5     public static void main(String[] args) {
 6         Service service = new Service();
 7         /**
 8          * A B 两个方法是同步的,因此访问A时候,要等A方法执行完,A线程释放锁,B线程才能拿到锁从而访问B方法。
 9          * 而C方法并不是同步的,可以异步执行
10          */
11         ThreadA threadA = new ThreadA(service);
12         threadA.start();
13         
14         ThreadB threadB = new ThreadB(service);
15         threadB.start();
16         
17         ThreadC threadC = new ThreadC(service);
18         threadC.start();
19     }
20 }
21 
22 class ThreadA extends Thread {
23 
24     private Service service;
25 
26     public ThreadA(Service service) {
27         this.service = service;
28     }
29 
30     public void run() {
31         super.run();
32         service.printA();
33     }
34 }
35 
36 class ThreadB extends Thread {
37 
38     private Service service;
39 
40     public ThreadB(Service service) {
41         this.service = service;
42     }
43 
44     public void run() {
45         super.run();
46         service.printB();
47     }
48 }
49 
50 class ThreadC extends Thread {
51 
52     private Service service;
53 
54     public ThreadC(Service service) {
55         this.service = service;
56     }
57 
58     public void run() {
59         super.run();
60         service.printC();
61     }
62 }
63 
64 class Service {
65     public synchronized void printA() {
66 
67         System.out.println("enter printA. thread name: "
68                 + Thread.currentThread().getName());
69 
70         try {
71             Thread.sleep(3000L);
72         } catch (InterruptedException e) {
73             // TODO Auto-generated catch block
74             e.printStackTrace();
75         }
76 
77         System.out.println("leaving printA. thread name: "
78                 + Thread.currentThread().getName());
79     }
80 
81     public synchronized void printB() {
82 
83         System.out.println("enter printB. thread name: "
84                 + Thread.currentThread().getName());
85 
86         System.out.println("leaving printB. thread name: "
87                 + Thread.currentThread().getName());
88     }
89 
90     public void printC() {
91 
92         System.out.println("enter printC. thread name: "
93                 + Thread.currentThread().getName());
94 
95         System.out.println("leaving printC. thread name: "
96                 + Thread.currentThread().getName());
97     }
98 }
synchronized方法

运行结果:

 

2) 拥有锁重入的功能,当一个线程获得一个对象锁之后 ,再次请求此对象锁的时候是可以再次获得的。

示例代码,修改上面示例代码,在A方法后面调用B方法,可以再次获得锁,运行结果:

 

3) 重写方法后,synchronized关键字并不继承。但没有被重写的方法,仍然是同步的。

  1 package com.khlin.thread;
  2 
  3 public class SynchronizedTest {
  4 
  5     public static void main(String[] args) {
  6         /**
  7          * 子类方法的实现若调用了父类的同步方法,一样有效
  8          */
  9         SubService service = new SubService();
 10         /**
 11          * A B 两个方法是同步的,因此访问A时候,要等A方法执行完,A线程释放锁,B线程才能拿到锁从而访问B方法。
 12          * 而C方法并不是同步的,可以异步执行
 13          */
 14         ThreadA threadA = new ThreadA(service);
 15         threadA.setName("threadA");
 16         threadA.start();
 17 
 18         ThreadB threadB = new ThreadB(service);
 19         threadB.setName("threadB");
 20         threadB.start();
 21 
 22         ThreadC threadC = new ThreadC(service);
 23         threadC.setName("threadC");
 24         threadC.start();
 25     }
 26 }
 27 
 28 class ThreadA extends Thread {
 29 
 30     private Service service;
 31 
 32     public ThreadA(Service service) {
 33         this.service = service;
 34     }
 35 
 36     public void run() {
 37         super.run();
 38         service.printA();
 39         // service.printB();
 40     }
 41 }
 42 
 43 class ThreadB extends Thread {
 44 
 45     private Service service;
 46 
 47     public ThreadB(Service service) {
 48         this.service = service;
 49     }
 50 
 51     public void run() {
 52         super.run();
 53         service.printB();
 54     }
 55 }
 56 
 57 class ThreadC extends Thread {
 58 
 59     private Service service;
 60 
 61     public ThreadC(Service service) {
 62         this.service = service;
 63     }
 64 
 65     public void run() {
 66         super.run();
 67         service.printC();
 68     }
 69 }
 70 
 71 class Service {
 72     public synchronized void printA() {
 73 
 74         System.out.println("enter printA. thread name: "
 75                 + Thread.currentThread().getName());
 76 
 77         try {
 78             Thread.sleep(3000L);
 79         } catch (InterruptedException e) {
 80             // TODO Auto-generated catch block
 81             e.printStackTrace();
 82         }
 83 
 84         System.out.println("leaving printA. thread name: "
 85                 + Thread.currentThread().getName());
 86 
 87         // printB();
 88     }
 89 
 90     public synchronized void printB() {
 91 
 92         System.out.println("enter printB. thread name: "
 93                 + Thread.currentThread().getName());
 94 
 95         System.out.println("leaving printB. thread name: "
 96                 + Thread.currentThread().getName());
 97     }
 98 
 99     public void printC() {
100 
101         System.out.println("enter printC. thread name: "
102                 + Thread.currentThread().getName());
103 
104         System.out.println("leaving printC. thread name: "
105                 + Thread.currentThread().getName());
106     }
107 }
108 
109 class SubService extends Service {
110 
111     public void printSubA() {
112         System.out.println("enter printSubA. thread name: "
113                 + Thread.currentThread().getName());
114         printA();
115         System.out.println("leaving printSubA. thread name: "
116                 + Thread.currentThread().getName());
117     }
118 }
View Code

运行结果和1)是一样的。

2. synchronized同步代码块

同步代码块,通过synchronized(一个对象)的写法,把一个代码块变成同步的。

其中,括号里待同步的对象,也可以使用this关键字,指向当前对象,这时与synchronized方法效果是一样的。

1) 被同步的对象是this的情况

与synchronized方法效果一样,示例代码:

 1 package com.khlin.thread;
 2 
 3 public class SynchronizedTestSecond {
 4     
 5     public static void main(String[] args) {
 6         ServiceSecond service = new ServiceSecond();
 7         
 8         ThreadAA threadAA = new ThreadAA(service);
 9         threadAA.setName("ThreadAA");
10         threadAA.start();
11         
12         try {
13             Thread.sleep(500L);
14         } catch (InterruptedException e) {
15             // TODO Auto-generated catch block
16             e.printStackTrace();
17         }
18         
19         ThreadBB threadBB = new ThreadBB(service);
20         threadBB.setName("ThreadBB");
21         threadBB.start();
22     }
23 
24 }
25 
26 class ServiceSecond {
27 
28     public void methodA() {
29         String threadName = Thread.currentThread().getName();
30         synchronized (this) {
31             System.out.println("invoke methodA. " + threadName);
32             try {
33                 Thread.sleep(1000L);
34             } catch (InterruptedException e) {
35                 // TODO Auto-generated catch block
36                 e.printStackTrace();
37             }
38             System.out.println("finish invoking methodA. " + threadName);
39         }
40     }
41 
42     public synchronized void methodB() {
43         String threadName = Thread.currentThread().getName();
44         System.out.println("invoke methodB. " + threadName);
45         System.out.println("finish invoking methodA. " + threadName);
46     }
47 }
48 
49 class ThreadAA extends Thread {
50     ServiceSecond service = new ServiceSecond();
51     
52     public ThreadAA(ServiceSecond service) {
53         this.service = service;
54     }
55     public void run() {
56         service.methodA();
57     }
58 }
59 
60 class ThreadBB extends Thread {
61     ServiceSecond service = new ServiceSecond();
62     
63     public ThreadBB(ServiceSecond service) {
64         this.service = service;
65     }
66     public void run() {
67         service.methodB();
68     }
69 }
View Code

运行结果:

2) 被同步的对象非this的情况

假设synchronized(x),此时,访问x对象里的同步方法,是同步访问的。

而访问主对象的同步方法,不受同步影响,属于异步。

让我们把上面的代码改为,synchronized(operation),锁住另一个对象。

 1 package com.khlin.thread;
 2 
 3 public class SynchronizedTestSecond {
 4     
 5     public static void main(String[] args) {
 6         ServiceSecond service = new ServiceSecond();
 7         
 8         /**
 9          * 锁住了operation对象,该对象的同步方法必须等待锁
10          * 而methodB并不属于该对象,ServiceSecond对象的锁,其实线程AA并没有获取,所以线程BB可以异步访问methodB方法。
11          */
12         
13         ThreadAA threadAA = new ThreadAA(service);
14         threadAA.setName("ThreadAA");
15         threadAA.start();
16         
17         try {
18             Thread.sleep(500L);
19         } catch (InterruptedException e) {
20             // TODO Auto-generated catch block
21             e.printStackTrace();
22         }
23         
24         ThreadBB threadBB = new ThreadBB(service);
25         threadBB.setName("ThreadBB");
26         threadBB.start();
27     }
28 
29 }
30 
31 class ServiceSecond {
32     
33     private ServiceOperation operation = new ServiceOperation();
34     
35     public void methodA() {
36         String threadName = Thread.currentThread().getName();
37         
38         synchronized (operation) {
39             System.out.println("invoke methodA. " + threadName);
40             try {
41                 Thread.sleep(1000L);
42             } catch (InterruptedException e) {
43                 // TODO Auto-generated catch block
44                 e.printStackTrace();
45             }
46             System.out.println("finish invoking methodA. " + threadName);
47         }
48     }
49 
50     public synchronized void methodB() {
51         String threadName = Thread.currentThread().getName();
52         System.out.println("invoke methodB. " + threadName);
53         System.out.println("finish invoking methodB. " + threadName);
54     }
55 }
56 
57 class ServiceOperation {
58     
59     public synchronized void methodOperation() {
60         System.out.println("this is operation method.");
61     }
62 }
63 
64 class ThreadAA extends Thread {
65     ServiceSecond service = new ServiceSecond();
66     
67     public ThreadAA(ServiceSecond service) {
68         this.service = service;
69     }
70     public void run() {
71         service.methodA();
72     }
73 }
74 
75 class ThreadBB extends Thread {
76     ServiceSecond service = new ServiceSecond();
77     
78     public ThreadBB(ServiceSecond service) {
79         this.service = service;
80     }
81     public void run() {
82         service.methodB();
83     }
84 }

运行结果:

因为线程BB在访问methodB的时候,service对象的锁并没有被占用着,所以可以直接占用并调用方法。

 

3. 静态方法的synchronized方法和synchronized(class)方法

这是对类加锁,注意,在java中,类是一种特殊的对象。

原理与上述的非静态方法同步是一样的,不再赘述。

示例代码:

 1 package com.khlin.thread;
 2 
 3 public class SynchronizedStatic {
 4     
 5     public static void main(String[] args) {
 6         ThreadAAA threadAAA = new ThreadAAA();
 7         threadAAA.setName("ThreadAAA");
 8         threadAAA.start();
 9         
10         try {
11             Thread.sleep(500L);
12         } catch (InterruptedException e) {
13             // TODO Auto-generated catch block
14             e.printStackTrace();
15         }
16         
17         ThreadBBB threadBBB = new ThreadBBB();
18         threadBBB.setName("ThreadBBB");
19         threadBBB.start();
20     }
21 }
22 
23 class StaticService {
24     public synchronized static void methodA()
25     {
26         String threadname = Thread.currentThread().getName();
27         
28         System.out.println("invoking methodA. " + threadname);
29         
30         try {
31             Thread.sleep(1000L);
32         } catch (InterruptedException e) {
33             // TODO Auto-generated catch block
34             e.printStackTrace();
35         }
36         
37         System.out.println("finish methodA. " + threadname);
38     }
39     
40     public synchronized static void methodB()
41     {
42         String threadname = Thread.currentThread().getName();
43         
44         System.out.println("invoking methodB. " + threadname);
45         
46         System.out.println("finish methodB. " + threadname);
47     }
48 }
49 
50 class ThreadAAA extends Thread {
51     
52     
53     public void run() {
54         StaticService.methodA();
55     }
56 }
57 
58 class ThreadBBB extends Thread {
59     
60     
61     public void run() {
62         StaticService.methodB();
63     }
64 }
View Code

运行结果:

 

以上是关于synchronized关键字小结的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点# Java | 关于synchronized相关理解

2020 年 Android 面试高频知识点小结 V1.0

2020 Java 面试题 小结 (答案慢慢补上,有错误请指出)

多线程线程安全问题

多线程线程安全问题

多线程线程安全问题