Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
当多条线程同时访问共享数据时,如果不进行同步,就会发生错误。Java提供的解决方案是:只要将操作共享数据的语句在某一时间段让一个线程执行完,在执行过程中其他线程不能进来执行。
使用synchronized时会有两种方式,一种是同步方法,一种是同步代码块
synchronized(this)是锁定当前对象实例。在函数m1()里面写synchronized(this ),这个和public synchronized void m1() 等价
对静态方法和静态变量使用synchronized方法,锁定的都是类,该类所有的实例都得排队等待已经取得锁的对象实例释放锁
类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。
synchronized(Object)锁定的是类中的成员变量,所有进行了synchronized(Object)的代码块都是互斥的
下面来看具体例子:
同一对象中,synchronized代码块和synchronized方法,锁定的对象
- 创建一个有4个方法的对象
syncBlock
,其中一个synchronized方法,一个synchronized代码块,锁定this对象,还有两个synchronized代码块,锁定syncBlock中的一个对成员变量 - 先启动一个线程(Thread-0), 并让其进入syncBlock对象的sychronized方法(add)内, 并使其停在synchronized方法内
- 再启动一个线程(Thread-1),并执行syncBlock对象的一个synchronized(this)代码块的方法(minus), 看看能否进入此方法内
- 再启动一个线程(Thread-2),并执行syncBlock对象的一个synchronized(processing)代码块的方法(times), 看看能否进入此方法内
- 再启动一个线程(Thread-3),并执行syncBlock对象的一个synchronized(processing)代码块的方法(division), 看看能否进入此方法内
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
150
151
152
153
154
155
156
157
158
159
160public class SyncBlock {
private int sum = 1;
private Object processing = new Object();
public synchronized void add() {
try {
System.out.println(Thread.currentThread().getName() + " add方法, 已经获取内置锁`SyncBlock`");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum += 100;
System.out.println(Thread.currentThread().getName() + " add方法, 即将释放内置锁`SyncBlock`");
}
public void minus() {
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + " minus方法, 已经获取内置锁`SyncBlock.this`");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum -= 80;
System.out.println(Thread.currentThread().getName() + " minus方法, 即将释放内置锁`SyncBlock.this`");
}
}
public void times() {
synchronized (processing) {
try {
System.out.println(Thread.currentThread().getName() + " times方法, 已经获取内置锁`processing`");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum *= 20;
System.out.println(Thread.currentThread().getName() + " times方法, 即将释放内置锁`processing`");
}
}
public void division() {
synchronized (processing) {
try {
System.out.println(Thread.currentThread().getName() + " division方法, 已经获取内置锁`processing`");
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum /= 2;
System.out.println(Thread.currentThread().getName() + " division方法, 即将释放内置锁`processing`");
}
}
public int getSum() {
return sum;
}
}
class BlockThread1 extends Thread {
SyncBlock syncBlock;
public BlockThread1(SyncBlock syncBlock) {
this.syncBlock = syncBlock;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " running ...");
syncBlock.add();
}
}
class BlockThread2 extends Thread {
SyncBlock syncBlock;
public BlockThread2(SyncBlock syncBlock) {
this.syncBlock = syncBlock;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " running ...");
syncBlock.minus();
}
}
class BlockThread3 extends Thread {
SyncBlock syncBlock;
public BlockThread3(SyncBlock syncBlock) {
this.syncBlock = syncBlock;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " running ...");
syncBlock.times();
}
}
class BlockThread4 extends Thread {
SyncBlock syncBlock;
public BlockThread4(SyncBlock syncBlock) {
this.syncBlock = syncBlock;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " running ...");
syncBlock.division();
}
}
class BlockThread5 extends Thread {
SyncBlock syncBlock;
public BlockThread5(SyncBlock syncBlock) {
this.syncBlock = syncBlock;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " running ...");
while (true) {
System.out.println("sum=" + syncBlock.getSum());
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TestSynBlock {
public static void main(String[] args) throws InterruptedException {
SyncBlock syncBlock = new SyncBlock();
BlockThread1 thread1 = new BlockThread1(syncBlock);
BlockThread2 thread2 = new BlockThread2(syncBlock);
BlockThread3 thread3 = new BlockThread3(syncBlock);
BlockThread4 thread4 = new BlockThread4(syncBlock);
BlockThread5 thread5 = new BlockThread5(syncBlock);
thread5.start();
thread1.start();//先执行, 以便抢占锁
Thread.sleep(500); //放弃cpu, 让thread1执行, 以便获的锁
thread2.start();//先执行, 以便抢占锁
Thread.sleep(500); //放弃cpu, 让thread2执行, 以便获的锁
thread3.start();//先执行, 以便抢占锁
Thread.sleep(500); //放弃cpu, 让thread3执行, 以便获的锁
thread4.start();//先执行, 以便抢占锁
Thread.sleep(500); //放弃cpu, 让thread4执行, 以便获的锁
}
}
程序输出如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23Thread-0 running ...
Thread-4 running ...
Thread-0 add方法, 已经获取内置锁`SyncBlock`
sum=1
Thread-1 running ...
Thread-2 running ...
Thread-2 times方法, 已经获取内置锁`processing`
sum=1
Thread-3 running ...
sum=1
Thread-2 times方法, 即将释放内置锁`processing`
Thread-3 division方法, 已经获取内置锁`processing`
sum=20
sum=20
Thread-0 add方法, 即将释放内置锁`SyncBlock`
Thread-1 minus方法, 已经获取内置锁`SyncBlock.this`
sum=120
sum=120
Thread-3 division方法, 即将释放内置锁`processing`
sum=60
Thread-1 minus方法, 即将释放内置锁`SyncBlock.this`
sum=-20
sum=-20
结果分析:
观察显示, 在输出Thread-1 running ...
后会暂停数秒(Thread-1无法获得锁而被挂起, 因为锁已经被Thread-0持有).
在输出Thread-3 running ...
后会暂停数秒(Thread-3无法获得锁而被挂起, 因为锁已经被Thread-2持有)
synchronized方法和synchronized(this)的代码块,二者取得的是同一个锁,都是当前对象的实例
synchronized(processing)代码块,二者取得的锁是processing对象