目录
- synchronized使用
- synchronized实现原理
- synchronized优化
一、synchronized使用
在JDK1.5的时候,synchronized被称作为重量级锁。他的效率远不如Lock。但是在JDK1.6的时候,sun对其做了很多优化,因此效率不在是一个考虑指标了,synchronized也是在那么重了。
synchronized可以修饰方法和作用于同步代码块两个地方,当修饰静态方法的时候,锁的是当前静态类,作用于实例方法上,锁的是当前实例对象。作用于同步代码块的时候,锁的是括号里面的对象,代码示例:
public class A{
//①锁的是当前类对象 class
public static synchronized void test1(){
//doto
}
//②锁的是当前实例对象
public synchronized void test2(){
//doto
}
//③锁的是括号里面的对象
public void test3(){
synchronized (object){
//todo
}
}
}
二、synchronized实现原理
1)同步代码块是使用 monitorenter 和 monitorexit 指令实现的;
2)同步方法(在这看不出来需要看JVM底层实现)依靠的是方法修饰符上的ACC_SYNCHRONIZED 实现。
任何对象都有一个 Monitor 与之相关联,当且一个 Monitor 被持有之后,他将处于锁定状态。
三、锁优化
在JDK1.6的时候,sun对synchrornized做了很多优化,使其性能得到了极大的提升。其中有自旋锁、锁消除、锁粗化、偏向锁和轻量级锁等
-
1.自旋锁
线程的阻塞和唤醒,需要 CPU 从用户态转为核心态。频繁的阻塞和唤醒对 CPU 来说是一件负担很重的工作,势必会给系统的并发性能带来很大的压力。同时,我们发现在许多应用上面,对象锁的锁状态只会持续很短一段时间。为了这一段很短的时间,频繁地阻塞和唤醒线程是非常不值得的。所以引入自旋锁。所谓自旋锁,就是让该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁。 -
2 锁消除
为了保证数据的完整性,我们在进行操作时需要对这部分操作进行同步控制。但是,在有些情况下,JVM检测到不可能存在共享数据竞争,这是JVM会对这些同步锁进行锁消除。如果不存在竞争,为什么还需要加锁呢?所以锁消除可以节省毫无意义的请求锁的时间。 -
3.锁粗化
我们知道在使用同步锁的时候,需要让同步块的作用范围尽可能小:仅在共享数据的实际作用域中才进行同步。这样做的目的,是为了使需要同步的操作数量尽可能缩小,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。在大多数的情况下,上述观点是正确的。但是如果一系列的连续加锁解锁操作,可能会导致不必要的性能损耗,所以引入锁粗话的概念。 -
4.偏向锁
通过观察,大多数情况下,同一个锁总是被一个线程持有,那么就记录下这个线程的threadId,然后把对象头的锁标志位设置为偏向锁,比对是否是同一个线程ID即可,如果有其他线程获得,那么偏向锁消失。 -
5.轻量级锁
轻量级锁主要就是用CAS,它是把对象头的标识拷贝一份到自己的栈里面,然后将引用指向栈里面的拷贝,当操作完成的时候,再用CAS更改回去,成功则表示竞争到了锁。失败就会升级成重量级锁。