JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法
多线程同时对资源进行访问时,同步机制使得同一时间内只能有一个线程对资源进行操作。
同步机制可以用Synchronized实现。
当Synchronized修饰一个方法的时候,该方法称为同步方法。
当Synchronized方法执行完成或者异常时会释放锁。
会有同学对synchronized修饰方法,静态方法,对象时具体对哪些东西加锁不是很明白,这里会进行详细的讲解。
-
synchronized修饰方法时,会对类实例进行加锁,该实例的所有synchronized方法必须等当前锁释放后才能访问。
-
synchronized修饰静态方法时,会对类对象上锁,改类的其他实例的的synchronized方法必须等当前锁释放后才能访问。
-
synchronized修饰对象时,会对对象上锁,其他使用该对象的synchronized方法必须等当前锁释放才能访问。
不使用synchronized修饰方法时
代码设计中一共使用到了三个类。
Example类是公共资源,会有多个线程访问。
本案例中有两个线程访问。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Example { public void exec() { for (int i = 0; i < 10; i++) { try { long time = (long) (Math.random() * 1000); Thread.sleep(time); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.printf("%s,Hello[%d]\n", Thread.currentThread().getName(), i); } }} |
MyThread类是线程类,用于访问Example公共资源。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
class MyThread extends Thread { private final Example example; public MyThread(Example example) { this.example = example; } @Override public void run() { example.exec(); }} |
TestSynchronized是主类,用于new 线程并启动。
|
1
2
3
4
5
6
7
8
9
|
public class TestSynchronized { public static void main(String[] args) { Example example = new Example(); MyThread thread1 = new MyThread(example); MyThread thread2 = new MyThread(example); thread1.start(); thread2.start(); }} |
由于现在的Example类的方法没有使用同步方法,所以两个线程会同时访问该方法,打印的值也是无序的,如下:
使用Synchronized修饰方法时
我们需要修改Example这个公共资源的方法。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Example { public synchronized void exec() { for (int i = 0; i < 10; i++) { try { long time = (long) (Math.random() * 1000); Thread.sleep(time); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.printf("%s,Hello[%d]\n", Thread.currentThread().getName(), i); } }} |
对于exec方法加上synchronized关键字,该方法同时只能有一个线程访问。
类中存在两个synchronized方法时
当类中存在多个synchronized方法时,线程访问synchronized方法时会对类的实例加锁,因此,该实例的其他synchronized方法在线程未结束之前无法访问。
我们对代码进行修改:
- Example类增加一个同步方法。
- 增加一个线程类MyThread2,用于访问Example的exec2()方法。
- 主线程创建两个线程,并启动。
执行结果如下:
synchronized修饰静态方法
synchronized修饰静态方法时,会对Class对象加锁。
对代码进行修改:
- Example方法增加一个同步static方法
- 在主线程新建两个Example的实例
- 启动两个线程并执行
当其中一个实例的exec方法没有执行结束时,另外一个实例的任何synchronized都无法执行,因为修饰静态方法锁的是Class对象,而不是锁Class的实例。
|
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
|
/******************************************************************************* * * COPYRIGHT (C) 2016 Tuniu Limited - ALL RIGHTS RESERVED. * * Creation Date: 2016年9月27日 * *******************************************************************************/package thread;/** * @author zhoujie8 * */public class TestSynchronized { public static void main(String[] args) { Example example = new Example(); MyThread thread1 = new MyThread(example); Example example2 = new Example(); MyThread2 thread2 = new MyThread2(example2); thread1.start(); thread2.start(); }}class MyThread extends Thread { private final Example example; public MyThread(Example example) { this.example = example; } @Override public void run() { example.exec3(); }}class MyThread2 extends Thread { private final Example example; public MyThread2(Example example) { this.example = example; } @Override public void run() { example.exec3(); }}class Example { public synchronized static void exec3() { for (int i = 0; i < 10; i++) { try { long time = (long) (Math.random() * 1000); Thread.sleep(time); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.printf("%s,Static[%d]\n", Thread.currentThread().getName(), i); } } public synchronized void exec() { for (int i = 0; i < 10; i++) { try { long time = (long) (Math.random() * 1000); Thread.sleep(time); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.printf("%s,Hello[%d]\n", Thread.currentThread().getName(), i); } } public synchronized void exec2() { for (int i = 0; i < 10; i++) { try { long time = (long) (Math.random() * 1000); Thread.sleep(time); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.printf("%s,world[%d]\n", Thread.currentThread().getName(), i); } }} |
运行结果如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Thread-1,Static[0]Thread-1,Static[1]Thread-1,Static[2]Thread-1,Static[3]Thread-1,Static[4]Thread-1,Static[5]Thread-1,Static[6]Thread-1,Static[7]Thread-1,Static[8]Thread-1,Static[9]Thread-0,Static[0]Thread-0,Static[1]Thread-0,Static[2]Thread-0,Static[3]Thread-0,Static[4]Thread-0,Static[5]Thread-0,Static[6]Thread-0,Static[7]Thread-0,Static[8]Thread-0,Static[9] |
synchronized修饰对象
synchronized修饰对象时锁住的是修饰的对象。
当一个线程执行时,将object对象锁住,另一个线程就不能执行对应的块。
我们需要设计一个例子:
- 类Example中需要有一个Object,需要两个同步方法,并且都是修饰该Object。
- 创建两个线程,同时访问这两个同步访问,看是否同步进行。
类Example代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Example { private final Object object; public Example(Object object) { this.object = object; } public void exec() { synchronized (object) { for (int i = 0; i < 10; i++) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.printf("%s,Hello%d\n", Thread.currentThread().getName(), i); } } }} |
Main方法中创建两个线程进行访问:
|
1
2
3
4
5
6
7
8
9
|
public static void main(String[] args) { Object object = new Object(); Example example = new Example(object); MyThread thread1 = new MyThread(example); MyThread2 thread2 = new MyThread2(example); thread1.start(); thread2.start();} |
结果输出如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Thread-0,Hello0Thread-0,Hello1Thread-0,Hello2Thread-0,Hello3Thread-0,Hello4Thread-0,Hello5Thread-0,Hello6Thread-0,Hello7Thread-0,Hello8Thread-0,Hello9Thread-1,Hello0Thread-1,Hello1Thread-1,Hello2Thread-1,Hello3Thread-1,Hello4Thread-1,Hello5Thread-1,Hello6Thread-1,Hello7Thread-1,Hello8Thread-1,Hello9 |




浙公网安备 33010602011771号