synchronized
synchronized关键字是java并发编程中必不可少的工具。它一次只允许一个线程进入特定代码段,从而避免多线程同时修改同一数据。
synchronized和内部锁
synchronized是围绕一个被称为内部锁或监视锁的内部实体实现的(Api规范里经常将其简单的称之为“monitor”)。内部锁在同步的两个方面发挥作用:强制独占访问对象状态和建立对可见性必不可少的happens-before关系。
每个对象都有一个与之关联的内部锁。按照惯例,一个需要排他和持有对象字段的线程在访问对象之前获得该对象的内部锁,在使用该对象过后释放该内部锁。一个线程获得锁和释放锁的过程称之为该线程持有该内部锁,只要有一个线程持有一个内部锁,其他线程就不能获得该锁,当其试图获取该锁时,将被阻塞。
当一个线程释放一个内部锁后,在紧随该操作之后的任何获得该锁的操作将于该操作建立happens-before关系
synchronized方法和synchronized语句
在java中synchronized关键字的使用有如下场景,以SynchorizedTest类举例:
public class SynchorizedTest {
//静态方法,锁住的是类对象
public static synchronized void staticMethod() {
...
}
//实例方法,锁住的是类的实例对象
public synchronized void method() {
...
}
public void methodClass() {
//同步代码块,锁住的是类对象
synchronized (SynchorizedTest.class) {
...
}
}
public void methodThis() {
//同步代码块,锁住的是类的实例对象
synchronized (SynchorizedTest.this) {
...
}
}
public void methoObject(Object object) {
//同步代码块,锁住的是配置的实例对象
synchronized (object) {
...
}
}
}
很明显,锁住的是类对象时,不同的实例之间也会受影响,锁住的是实例对象时,只有同一实例才起作用。
可重入同步
一个线程不能持有另一个线程的持有锁,但其可以获得它已经持有的锁。允许一个线程多次获得相同的锁可以实现可重入同步。这描述了这样一种情况:同步代码直接或间接地调用同样包含同步代码的方法,并且两组代码使用相同的锁。如果没有可重入同步,同步代码将不得不采取许多额外的预防措施,以避免线程导致自身阻塞。如下面代码:
public class Main {
public static void main(String[] args) {
SynchorizedTest test = new SynchorizedTest();
test.methodThis();
}
static class SynchorizedTest {
public synchronized void method() {
System.out.println("method");
}
public void methodThis() {
synchronized (SynchorizedTest.this) {
System.out.println("statementsThis");
method();
}
}
}
}
//输出结果:
statementsThis
method
主线程在调用实例化了一个SynchorizedTest类的对象test,在调用test的methodThis方法时持有了该实例的内部锁,接着在还未释放的情况下有调用了需要持有内部锁的method方法,输出结果展示了内部锁可重入锁,不会产生阻塞。
Collections.synchronizedList()
了解了上面的知识后,稍微来分析下Collections类中的synchronizedList方法是怎么实现同步List的,首先,是synchronizedList方法源码:
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
可以看到这里会判断list是否继承自RandomAccess,是则实例化一个SynchronizedRandomAccessList对象,否则实例化SynchronizedList对象,RandomAcces表示可以快速随机访问,这里我们不多关注,所以主要关注SynchronizedList的实现,其源码如下:
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return list.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return list.hashCode();}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
public int indexOf(Object o) {
synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {
synchronized (mutex) {return list.lastIndexOf(o);}
}
public boolean addAll(int index, Collection<? extends E> c) {
synchronized (mutex) {return list.addAll(index, c);}
}
public ListIterator<E> listIterator() {
return list.listIterator(); // Must be manually synched by user
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index); // Must be manually synched by user
}
public List<E> subList(int fromIndex, int toIndex) {
synchronized (mutex) {
return new SynchronizedList<>(list.subList(fromIndex, toIndex),
mutex);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
synchronized (mutex) {list.replaceAll(operator);}
}
@Override
public void sort(Comparator<? super E> c) {
synchronized (mutex) {list.sort(c);}
}
private Object readResolve() {
return (list instanceof RandomAccess
? new SynchronizedRandomAccessList<>(list)
: this);
}
}
可以看出SynchronizedList是List的装饰类,其主要方法都锁住了一个名为mutex的对象,该对象在SynchronizedList的父类SynchronizedCollection中定义,如下:
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}
SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}
...
}
从SynchronizedCollection的两个构造方法可以看出,其锁住的要么是该类的实例,要么是传入的对象。
网友评论