- 目录
- 前言
- 场景
- 知其所以然
- 注意
- 小结
前言
关于synchronized这个关键字,笔者这里不想像书中一样,用大量的示例来做演示,希望通过一个例子,作归纳和总结。
场景
已知存在一个类Sample(该类有两个实例对象sampleAlpha,sampleBeta),如下图所示。
public class Sample {
private Object lock = new Object();
public synchronized void one() {...}
public synchronized void two() {...}
public void three() {...}
public void four() {
synchronized (this) {
...
}
}
public void five() {
synchronized (lock) {
...
}
}
public static synchronized void six() {
...
}
public void seven() {
synchronized (Sample.class) {
...
}
}
}
下面我们来分不同场景讨论下同步的问题:
同一个实例对象:
1.线程A执行实例对象
sampleAlpha
中的one
方法,同时线程B执行实例对象sampleAlpha
中的two
方法,同步执行。
2.线程A执行实例对象sampleAlpha
中的one
方法,同时线程B执行实例对象sampleAlpha
中的three
方法,异步执行。
3.线程A执行实例对象sampleAlpha
中的one
方法,同时线程B执行实例对象sampleAlpha
中的four
方法,同步执行。
4.线程A执行实例对象sampleAlpha
中的one
方法,同时线程B执行实例对象sampleAlpha
中的five
方法,异步执行。
5.线程A执行实例对象sampleAlpha
中的one
方法,同时线程B执行实例对象sampleAlpha
中的six
方法,异步执行。
6.线程A执行实例对象sampleAlpha
中的one
方法,同时线程B执行实例对象sampleAlpha
中的seven
方法,异步执行。
7.线程A执行实例对象sampleAlpha
中的six
方法,同时线程B执行实例对象sampleAlpha
中的seven
方法,同步执行。
不同的实例对象:
8.线程A执行实例对象
sampleAlpha
中的one
方法,同时线程B执行实例对象sampleBeta
中的two
方法,异步执行。
9.线程A执行实例对象sampleAlpha
中的six
方法,同时线程B执行实例对象sampleBeta
中的seven
方法,同步执行。
……其他类似情况,不一一列举
知其所以然
为什么会出现上述的几种不同情况呢?下面来具体讨论:
第1种:
线程A
执行方法one的时候已经持有了该方法所属对象的锁
,当线程B执行方法two
的时候,会和线程A竞争该对象的锁,进入等待状态,当线程A释放该对象的锁
后,线程B继续持有该锁,继续执行。
第2种:线程A
执行方法one的时候已经持有了该方法所属对象的锁
,当线程B执行方法three
的时候,并没有同步锁,不需要竞争,直接执行完毕。
第3种:synchronized(this)
获取的也是该对象的锁,参考第1种方式。
第4种:线程A
执行方法one的时候已经持有了该方法所属对象的锁
,当线程B执行方法five
的时候,线程B持有的是另外一个对象锁lock
,不会和线程A产生竞争,故异步执行。
第5种:线程A
执行方法one的时候已经持有了该方法所属对象的锁
,当线程B执行方法six
的时候,此时线程B持有的不是该对象的锁,而是Class类锁
,和线程A竞争的不是同一个,故而异步执行。
第6种:同第5种解释。
第7种:同步执行的原因在于静态同步synchronized方法
和synchronized(class)代码块
,都是获取当前Class类
的锁,对该类产生的所有对象实例起作用。
第8种:由于线程A和线程B持有的都不是同一个对象的锁,故而异步执行,不会产生同步。
第9种:由于线程A和线程B虽然操作的是不同的对象实例,但是他们持有的都是同一个Class类
的锁,故而产生竞争,同步执行。
注意
1.由于常量池的原因,大多数情况下
synchronized代码块
都不要使用String作为锁对象,最好使用new Object()
实例化一个对象。
2.关键字synchronized不具有继承性
,但是可以在子类中标记该关键字,在父子继承关系中,实现可重入锁。
3.当线程执行出现异常时,其所持有的锁会自动释放。
小结
从上面的几个例子,我们明白了,synchronized修饰方法和synchronized(this)获取的都是当前实例对象的锁
,synchronized静态方法和synchronize(class)获取的是Class类
的锁,这是两种不同的锁,而要想多线程产生同步,则多个线程竞争的必须是同一个锁
。
牢记这一点,做一个心中有锁的人。