视频地址:https://www.bilibili.com/video/BV1Jx411a7Dp
若
synchronized
修饰的代码块中出现异常,线程进行异常处理后会马上释放锁(与ReentrantLock
正相反)。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
30public class Test {
int i = 0;
// 同步方法,计数到5抛出异常
synchronized void m() {
System.out.println(Thread.currentThread().getName() + " start");
while (true) {
i++;
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 计数到5抛出异常
if (i == 5) {
int error = 1 / 0;
}
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t::m, "线程1").start();
new Thread(t::m, "线程2").start();
}
}线程1抛出异常后释放锁,线程2得到锁开始执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19线程1 start
线程1: 1
线程1: 2
线程1: 3
线程1: 4
线程1: 5
线程2 start
线程2: 6
Exception in thread "线程1" java.lang.ArithmeticException: / by zero
at Test.m(Test.java:23)
at java.lang.Thread.run(Thread.java:748)
线程2: 7
线程2: 8
线程2: 9
线程2: 10
线程2: 11
线程2: 12
线程2: 13
线程2: 14synchronized
锁住的是堆中对象的实例,而不是对象的引用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
38public class Test {
Object o = new Object();
// 该方法锁住的o对象引用没有被设为final
void m() {
synchronized (o) {
while (true) {
System.out.println(Thread.currentThread().getName() + "正在运行");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t::m, "线程1").start();
// 在这里让程序睡一会儿,保证两个线程得到的o对象不同
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(t::m, "线程2");
// 改变锁引用,使得线程2也有机会运行,否则一直都是线程1运行
t.o = new Object();
thread2.start();
}
}1
2
3
4
5
6
7
8
9
10线程1正在运行
线程1正在运行
线程1正在运行
线程2正在运行
线程1正在运行
线程2正在运行
线程1正在运行
线程2正在运行
线程1正在运行
线程2正在运行主线程睡了3秒之后,
线程1
和线程2
交替运行,他们各自抢到了不同的锁。synchronized
方法和非synchronized
方法可以同时执行非
synchronized
方法不需要抢这把锁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
33public class Test {
// 同步方法
public synchronized void m1() {
System.out.println(Thread.currentThread().getName() + " m1 start");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " m1 end");
}
// 非同步方法
public void m2() {
System.out.println(Thread.currentThread().getName() + " m2 start");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " m2 end");
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t::m1).start();
new Thread(t::m2).start();
}
}1
2
3
4Thread-0 m1 start
Thread-1 m2 start
Thread-1 m2 end
Thread-0 m1 end同步方法
m1()
执行的同时,非同步方法m2()
也在执行。不要以字符串常量作为锁定对象:
因为字符串常量池的存在,两个不同的字符串引用可能指向同一字符串对象
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
32public class Test {
// 两个字符串常量,作为两同步方法的锁
String s1 = "Hello";
String s2 = "Hello";
// 同步m1方法以s1为锁
void m1() {
synchronized (s1) {
while (true) {
System.out.println(Thread.currentThread().getName() + ":m1 is running");
}
}
}
// 同步m2方法以s2为锁
void m2() {
synchronized (s2) {
while (true) {
System.out.println(Thread.currentThread().getName() + ":m1 is running");
}
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t::m1, "线程1").start();
new Thread(t::m2, "线程2").start();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running
线程1:m1 is running两个字符串常量指向的是同一对象,有一个线程永远得不到锁。
synchronized
是可重入锁同一线程内同步方法之间可以相互调用
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
27public class Test {
synchronized void m1() {
System.out.println("m1 start");
m2(); // 在同步方法m1()中调用同步方法m2(),不会发生死锁,因为这是在同一线程内的调用
System.out.println("m1 end");
}
// 另一个同步方法
synchronized void m2() {
System.out.println("m2 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2 end");
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t::m1).start();
}
}1
2
3
4m1 start
m2 start
m2 end
m1 end子类的同步方法可以调用父类的同步方法也不会发生死锁
两个方法锁住的
this
指向的都是同一个子类对象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
30class TestF {
// 父类同步方法
synchronized void m() {
System.out.println("father method start");
System.out.println("father method lock:" + this);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("father method end");
}
}
public class Test extends TestF {
synchronized void m() {
System.out.println("child method start");
System.out.println("child method lock:" + this);
super.m();
System.out.println("child method end");
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t::m).start();
}
}1
2
3
4
5
6child method start
child method lock:Test@1080cc6
father method start
father method lock:Test@1080cc6
father method end
child method end