本文共 3367 字,大约阅读时间需要 11 分钟。
参考网址:
作用
保证原子性、可见性。
只要保证多个线程使用同一个“对象监视锁”,就能保证同步。用法
用法 | 对象监视器 | 示例 |
修饰普通方法 | 当前实例对象 | public synchronized void increase() { i++; } |
修饰静态方法 | 当前类的class对象 | public static synchronized void increase() { i++; } |
修饰代码块 | 括号里边的对象。 这个对象可以是实例对象(例如this),也可以是class对象(例如:XXX.class或者this.getClass())。 | public Object synMethod(Object a1) { synchronized(a1) { // 操作 } } |
源码探寻
Test.java
public class Test { private static Object LOCK = new Object(); public static int main(String[] args) { synchronized (LOCK){ System.out.println("Hello World"); } return 1; }}
先用javac Test.class 编译出class文件
再用javap –c Test.class查看字节码文件字节码文件:
也就是说,锁是通过monitorenter和monitorexit来实现的。
进入监视器
JVM规范中描述:
monitorenter:` Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership. `翻译:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
退出监视器
JVM规范中描述:
monitorexit: ` The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.`翻译:
执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
比较方面 | synchronized | volatile |
作用 | 锁定当前对象,只有当前线程可以访问该对象,其他线程被阻塞。 | 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; |
使用范围 | 变量、方法、和类 | 变量 |
线程安全 | 可以保证变量的可见性和原子性 | 仅能实现变量的可见性,不能保证原子性 |
阻塞 | 可能会造成线程的阻塞 | 不会造成线程的阻塞 |
优化 | 标记的变量可以被编译器优化 | 标记的变量不会被编译器优化 |
参考网址:
项 | synchronized | Lock |
存在层面 | java 内置关键字,在 jvm 层面 | java 类(实际是一个接口),在API层面 |
锁的释放 | 会自动释放锁: 线程执行完同步代码会释放锁 ; 线程执行中发生异常会释放锁 | 需在 finally 中手工释放锁(unlock()方法释放锁) |
锁的获取 | 若线程 1 获得锁,线程 2 等待; 若线程 1 阻塞,线程 2 会一直等待 | 分情况而定,Lock有多个获得锁的方式。 可尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断是否已经获取锁 | 可判断是否已经获取到锁 |
锁类型 | 可重入、不可中断、非公平 (只能等待锁释放,不能响应中断) | 可重入、可中断、可公平(两者皆可) (等待锁时可用interrupt来中断等待) |
适用场景 | 大量同步的代码 | 少量代码的同步 |
性能 | jdk1.6以前:重量级锁(无法取得锁即挂起) jdk1.6之后:优化了性能:给它的锁加入了四种状态,无锁状态 -> 偏向锁 -> 轻量级锁 -> 重量级锁,自动进行锁的升级。 | jdk1.6以后: 竞争资源不激烈时:两者性能差不多 竞争资源激烈时:Lock性能远远好于synchronized |
参考网址:
相同点
1. 它们都是加锁方式同步;
2. 都是可重入锁; 3. 阻塞式的同步;不同点
因为ReentrantLock实现了Lock,所以拥有synchronized与Lock的所有不同点,其他不同点如下:
1. 多个线程在等待的时候,可以提供公平的锁;默认的是非公平锁,性能会比公平锁好一些; 2. ReenTrantLock 可以绑定多个锁条件转载地址:http://dhvjz.baihongyu.com/