本文由Scarb发表于金甲虫的博客,转载请注明出处

synchronized

synchronized statements must specify the object that provides the intrinsic lock:

1
2
3
4
5
6
7
8
> public void addName(String name) {
> synchronized(this) {
> lastName = name;
> nameCount++;
> }
> nameList.add(name);
> }
>

In this example, the addName method needs to synchronize changes to lastName and nameCount, but also needs to avoid synchronizing invocations of other objects’ methods. (Invoking other objects’ methods from synchronized code can create problems that are described in the section on Liveness.) Without synchronized statements, there would have to be a separate, unsynchronized method for the sole purpose of invoking nameList.add.

Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. All updates of these fields must be synchronized, but there’s no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> public class MsLunch {
> private long c1 = 0;
> private long c2 = 0;
> private Object lock1 = new Object();
> private Object lock2 = new Object();
>
> public void inc1() {
> synchronized(lock1) {
> c1++;
> }
> }
>
> public void inc2() {
> synchronized(lock2) {
> c2++;
> }
> }
> }
>

Use this idiom with extreme care. You must be absolutely sure that it really is safe to interleave access of the affected fields.

synchronized实现

在synchronize对象上加锁

  • synchonized(Object)
    • 不能锁定常量

底层实现:

  • JDK早期:重量级,向OS申请锁
  • JDK1.5之后:锁升级
    1. 偏向锁:HotSpot实现:Object头上(markword)记录线程ID,实际没有加锁
    2. 自旋锁:如果线程争用,升级为自旋锁:争用线程循环等待(10次)
    3. 重量级锁:循环等待10次后升级为重量级锁

加锁代码执行时间短、线程数少的情况下用自旋锁

加锁代码执行时间长、线程数多的情况下用系统锁

synchronized方法

1
2
3
public synchronized void increment() {
c++;
}

相当于

1
2
3
4
5
public void increment() {
synchronized(this) {
c++;
}
}

1
2
3
public synchronized static void increment() {
c++;
}

相当于

1
2
3
4
5
public static void increment() {
synchronized(T.class) {
c++;
}
}

synchronized可重入

两个synchronized方法m1m2,在m1中可以调用m2

一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁。

也就是说synchronized获得的锁是可重入的

锁异常释放

程序在执行过程中,如果出现异常,默认情况锁会被释放

所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。

比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。

因此要非常小心的处理同步业务逻辑中的异常