synchronized 关键字浅析
synchronized 关键字概述
Java 平台中的任何一个对象都有唯一一个与之关联的锁。这种锁被称为监视器或者内部锁。内部锁是一种排他锁,它能够保障原子性、可见性和有序性。
内部锁是通过 synchronized 关键字实现的。synchronized 关键字可以用来修饰方法以及代码块。
synchronized 关键字修饰的方法就被称为同步方法。synchronized 修饰的静态方法就被称为静态同步方法,synchronized 修饰的实例方法就被称为实例同步方法。同步方法的整个方法体就是一个临界区。
synchronized 关键字修饰的代码块被称为同步块,其语法如下所示:
1
2
3
synchronized(锁句柄){
}
注意: 作为锁句柄的变量通常采用 private final 修饰,如:private final Object lock = new Object();
synchronized 实例方法和 synchronized 代码块
假设有如下 synchronized 实例方法。
1
2
3
synchronized void method() {
}
这跟下面将方法体用 synchronized 代码块包围起来是等效的。
1
2
3
4
5
void method() {
synchronized (this) {
}
}
也就是说,synchronized 实例方法是使用 this 的锁来执行线程的互斥处理的。
synchronized 静态方法和 synchronized 代码块
synchronized 关键字线程八锁
- (1)
- (2)
- (3)
- (4)
- (5)
- (6)
- (7)
- (8)
- (9)
- (10)
- (11)
- (12)
两个普通同步方法,两个 Number 对象?不同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test {
public static void main(String[] args) {
Number number1 = new Number();
Number number2 = new Number();
new Thread(() -> {
number1.getOne();
}).start();
new Thread(() -> {
number2.getTwo();
}).start();
}
}
class Number {
public synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public synchronized void getTwo() {
System.out.println("two");
}
}
两个静态同步方法,两个 Number 对象?同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Test {
public static void main(String[] args) {
Number number1 = new Number();
Number number2 = new Number();
new Thread(() -> {
number1.getOne();
}).start();
new Thread(() -> {
number2.getTwo();
}).start();
}
}
class Number {
public static synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public static synchronized void getTwo() {
System.out.println("two");
}
}
一个静态同步方法,一个非静态同步方法,两个 Number 对象?不同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Test {
public static void main(String[] args) {
Number number1 = new Number();
Number number2 = new Number();
new Thread(() -> {
number1.getOne();
}).start();
new Thread(() -> {
number2.getTwo();
}).start();
}
}
class Number {
public static synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public synchronized void getTwo() {
System.out.println("two");
}
}
两个静态同步方法,一个 Number 对象?同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test {
public static void main(String[] args) {
Number number = new Number();
new Thread(() -> {
number.getOne();
}).start();
new Thread(() -> {
number.getTwo();
}).start();
}
}
class Number {
public static synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public static synchronized void getTwo() {
System.out.println("two");
}
}
两个普通同步方法,一个 Number 对象?同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
public static void main(String[] args) {
Number number = new Number();
new Thread(() -> {
number.getOne();
}).start();
new Thread(() -> {
number.getTwo();
}).start();
}
}
class Number {
public synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public synchronized void getTwo() {
System.out.println("two");
}
}
一个静态同步方法,一个非静态同步方法,一个 Number 对象?不同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
public static void main(String[] args) {
Number number = new Number();
new Thread(() -> {
number.getOne();
}).start();
new Thread(() -> {
number.getTwo();
}).start();
}
}
class Number {
public static synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public synchronized void getTwo() {
System.out.println("two");
}
}
一个(静态 or 非静态)同步方法,一个(静态 or 非静态)非同步方法,一个 Number 对象?不同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
public static void main(String[] args) {
Number number = new Number();
new Thread(() -> {
number.getOne();
}).start();
new Thread(() -> {
number.getTwo();
}).start();
}
}
class Number {
public synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public void getTwo() {
System.out.println("two");
}
}
一个(静态 or 非静态)同步方法,一个(静态 or 非静态)非同步方法,两个 Number 对象?不同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test {
public static void main(String[] args) {
Number number1 = new Number();
Number number2 = new Number();
new Thread(() -> {
number1.getOne();
}).start();
new Thread(() -> {
number2.getTwo();
}).start();
}
}
class Number {
public synchronized void getOne() {
System.out.println("one");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}
public void getTwo() {
System.out.println("two");
}
}
synchronized 关键字的特点
- 可重入性,当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功。
从互斥锁的设计上来说,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功,在 Java 中 synchronized 是基于原子性的内部锁机制,是可重入的,因此在一个线程调用 synchronized 方法的同时在其方法体内部调用该对象另一个 synchronized 方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,这就是 synchronized 的可重入性。
- 自动释放锁,当代码段执行结束或出现异常后会自动释放对监视器的锁定。
- 非公平锁,在等待获取锁的过程中不可被中断。
- 互斥性,被 synchronized 修饰的方法同时只能由一个线程执行。
synchronized 关键字的使用
普通方法和代码块中使用 this 是同一个监视器(锁),即某个具体调用该代码的对象。
静态方法和代码块中使用该类的 class 对象是同一个监视器,任何该类的对象调用该段代码时都是在争夺同一个监视器的锁定。
注意:作为锁句柄的变量通常采用 private final 修饰,如:private final Object lock = new Object();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
public class SynchronizedTest implements Runnable {
private static int i = 0;
public static synchronized void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new SynchronizedTest());
Thread t2 = new Thread(new SynchronizedTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
SynchronizedTest test = new SynchronizedTest();
new Thread(test::method1).start();
new Thread(test::method2).start();
}
}
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
SynchronizedTest test = new SynchronizedTest();
new Thread(test::method1).start();
new Thread(test::method2).start();
}
}
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
SynchronizedTest test1 = new SynchronizedTest();
SynchronizedTest test2 = new SynchronizedTest();
new Thread(test1::method1).start();
new Thread(test2::method2).start();
}
}
public class SynchronizedTest implements Runnable {
private static final SynchronizedTest instance = new SynchronizedTest();
private static int i = 0;
@Override
public void run() {
synchronized (instance) {
for (int j = 0; j < 10000000; j++) {
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
//this锁的是当前实例对象
synchronized (this) {
for (int j = 0; j < 1000000; j++) {
i++;
}
}
//class锁的是当前类对象
synchronized (AccountingSync.class) {
for (int j = 0; j < 1000000; j++) {
i++;
}
}
synchronized 关键字常见面试题
- Java 中有哪些锁?synchronized 与 Lock 有哪些区别?什么是公平锁和非公平锁?他们的底层怎么实现的?
- 谈谈 synchronized 关键字的理解?synchronized 的实现,锁的升级过程。synchronized 什么情况下会释放锁?
- synchronized 能否锁静态方法,synchronized 修饰同一个类的两个静态方法同步吗,为什么。
- 如果一个对象有多个方法加了 synchronized,那么该对象有几把锁。【一把锁】
- synchronized 分别修饰在实例方法上和静态方法上,多线程并发时是否会竞争锁?synchronized 修饰在方法上和代码块上有什么区别,底层是怎么实现的?
- Lock 接口的实现类 ReentrantLock 和 synchronized 有什么区别?ReentrantReadWriteLock 呢?
- 说下 synchronized 的用法,说下 JDK 对 synchronized 的优化?
- synchronized 关键字的作用、原理、锁升级、锁粗化、锁消除、偏向锁、轻量级锁、重量级锁。
- 为什么要用 synchronized?原理?效率?
- synchronized 是可重入锁么。synchronized 为什么说他比较重。
- volatile、synchronized 的用法,比如 synchronized 在静态方法和普通方法上面时,能否同时进入?
- volatile、synchronized、Lock 的区别?
- 说说你用到 volatile 和 synchronized 的一些场景。(说了个双重校验单例模式 DCL)
- synchronized 的底层实现原理以及和 volatile 的区别?他们能保证原子性吗?能保证可见性吗?
- CAS 和 synchronized 有什么区别?都用 synchronized 不行么?
- 说一下 Java 的内存模型?说一下 volatile 关键字?他的原理是什么?volatile 是线程安全的吗?说一下 volatile 和 synchronized 这两个关键字的区别?