Java多线程-synchronized的使用

目录
  1. 相关概念
    1. 注意
  2. 使用(同步方法与同步代码块)
    1. 方法同步
    2. 代码块同步

Java的同步主要是通过synchronized和Lock来实现。
本文主要说的是Java关键字synchronized的使用。

相关概念

  • 监视器
    在Java中每个对象都有一个锁标记(monitor),也称监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。

在Java中,可以使用关键字synchronized来标记一个方法或者代码块。

注意

synchronized可修饰方法、代码块,但不可去修饰属性、构造方法。

使用(同步方法与同步代码块)

方法同步

1
2
3
4
// 方法同步
public synchronized void test() {
// do something ...
}

synchronized用在方法上,则线程调用此方法时会获取该实例的对象锁,方法未结束之前其他线程只能等待。

同时其他线程也无法访问该实例的其他synchronized方法,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程就无法获取该对象锁。

同理,当一个线程访问该实例的synchronized方法时,其他线程能访问该对象的非synchronized方法,因为没使用到对象锁。

上面的前提是各线程调用的是同一对象实例,否则synchronized无效。

但若该synchronized方法为类方法,即方法修饰符为static,那么使用synchronized调用此方法的线程会获取该类的锁,只要该线程没有退出当前方法,其他线程也无法访问该方法。又若一个线程访问的是该对象的普通(非static)synchronized方法,另一个线程访问的是该对象(该类)的static synchronized方法,也不会发生互斥现象。因为它们一个获取的是对象锁,另一个是类锁,不冲突。

代码块同步

1
2
3
4
5
6
7
8
public void test2() {
...
// 代码块同步
synchronized(object) {
// do something ...
}
...
}

而用在代码块上,当线程运行到该代码块时,就会获得object对象的对象锁,如果多线程使用同一个object对象,那么此时就会同步,使得其他线程无法同时访问该代码块。使用synchronized代码块只对需要的部分进行同步,效率也更高。
当object为this时,代表获取的是当前实例的锁。即

1
2
3
4
5
6
7
8
9
10
11
12
public void test3() {
...
synchronized(this) {
// do something ...
}
...
}
// 此时,其效果等同于
public synchronized void test4() {
// do something ...
}

最后,使用synchronized出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。