关于 Synchronized 的原理可以参见 聊聊并发(二)Java SE1.6中的Synchronized
概念
- 首先要强调的一点 Synchronized 并不是锁它只是一个关键字,他的作用是标记方法或者代码块是同步的。Synchronized 关键字会直接锁住公共内存,保证资源、数据的同步避免竞争。
- 本质上 Synchronized 锁住的是一个叫 Monitor 的东西,如果一个 Monitor 被 Synchronized 标记,所有访问此 Monitor 的动作都会被阻塞直到 Monitor 被释放掉。
- 被 Synchronized 标记的代码块执行完毕后会自动释放 Monitor 即解除锁。
- Synchronzied 是一个很重的方法。
Synchronized 大概可以分为以下几种同步块:
-
标记实例方法:实例同步方法
在方法声明的时候加上 Synchronized 关键字,这将告诉 Java 此方法是同步的,此时 Synchronized 的 Monitor 是拥有该方法的对象。所以当前线程可以访问此类的任何实例方法, 而其他线程需要等待 methodA() 结束。
这样每一个 methodA() 方法都同步在不同的对象上,即该方法所属的实例。如果有多个实例存在,那么一个线程一次可以在一个实例同步块中执行,即一个实例一个线程。
public class SynchronizedDemo {
/**
* 此时 synchronized 锁住的是当前对象!
*/
public synchronized void methodA() {
……
}
}
-
标记静态方法 :静态同步方法
静态方法同步和实例方法同步方法一样,也使用synchronized 关键字。被 static 标记的方法也称类方法,他是属于类的而非具体对象,所以如果 Synchronized 方法标记了静态方法,此时的 Monitor 将会是 SynchronizedDemo 的字节码文件( SynchronizedDemo.class 也就是类对象),在 Java 虚拟机中一个类只有一个类对象,这意味着不管同一个类中的哪个静态同步方法被访问,其他的线程都需等待。即一个字节码文件一个线程。
对于不同类的静态同步方法,他们的 Monitoer 为各自的字节码文件,所以一个线程可以同时访问不同类中的竟然方法而无需等待。
public class SynchronizedDemo {
/**
* staticA 的写法等价于 staticB
*/
public static synchronized void staticA() {
}
public static void staticB() {
synchronized (SynchronizedDemo.class){
}
}
}
// 此方式将会锁住当前实例
synchronized (this) {
}
// 锁住字节码文件,意味着 SynchronizedDemo 的所有实例都会被锁住
synchronized (SynchronizedDemo.class) {
}
// 锁住指定的实例,这种方式一般用於单独判读
synchronized (monitor) {
}
public class SynchronizedDemo {
/**
* staticA 、staticB 写法等价
*/
public static synchronized void staticA() {
}
public static void staticB() {
synchronized (SynchronizedDemo.class){
}
}
}