Java多线程 - synchronized

326 阅读2分钟

线程

线程是什么?

  • 线程由Thread ID, PC 程序计数器, Registers 寄存器和堆栈组成。是CPU使用资源的最小单位。
  • 由图可以看出,一个进程中的线程们共享资源和内存,为了不让线程们对共享资源的操作出现混乱,我们需要一个机制来管理,这个机制就是

锁是什么?

根据维基百科,锁是一种同步机制,用于在执行多线程时强行限制线程们对共享资源的访问。

翻译一下来说,假设有个线程叫小明,还有个线程叫小军,他俩同时想上同一个卫生间,总不能让他们共用一个马桶吧,所以小明先进去,把卫生间上锁,等小明完事了,解锁,然后小军再进去,这就保证了同一时刻,只能有一个人使用同一个马桶.

synchronized

What?

synchronized是Java提供的关键字,对一个对象实现线程独占,进行原子操作。

How?

  1. 方法锁
    锁住的是当前对象,所以只有使用同一对象的线程才会受到影响。也就是说,多个对象调用operate()方法,不会收到锁的影响。
    如果有多个synchronized修饰的方法,它们会被一起锁住。
public class ClassA {
    public synchronized operate(){
    }
}
  1. 对象锁
    示例代码锁住的是一个代码块,所以又叫同步代码块锁 任何想要使用operate()方法的线程,需要先获得object实例的锁
// 使用在函数中
public class ClassA {
    Object obj = new Object();
    public operate(){
        synchronized(obj) {        
            // 该块内为原子操作
        }
    }
}
  1. 静态锁 不论锁的是静态方法,还是静态对象,还是.class(类锁),因为是静态的,JVM中只存在一个,实现了全局锁定
public class ClassA {
    public synchronized static operate(){
    }
}
public class ClassA {
    static Object obj = new Object(); 
    public operate(){
    	synchronized (obj) {
        }
    }
}
public class ClassA {
    public operate(){
    	synchronized (ClassA.class) {
        }
    }
}

锁的释放

  • 被锁住的代码运行完成
  • 抛出异常,自动释放

特性

  • 不可中断
  • 可重入

加锁原理

观察如下代码进行反编译后的字节码:

Object o = new Object();
synchronized (o) {
    System.out.println("hello");
}
 0 new #2 <java/lang/Object>
 3 dup
 4 invokespecial #1 <java/lang/Object.<init>>
 7 astore_1
 8 aload_1
 9 dup
10 astore_2
11 monitorenter
...
21 monitorexit
...
30 return

从汇编角度看,monitorenter指令代表加锁,monitorexit指令代表解锁