1、synchronized使用
synchronized是java提供的关键字,用来实现同步,主要用法有以下几种:
- 同步代码块,锁是括号里面的对象
synchronized(object) {
//在此代码中访问共享数据
}
- 同步方法,锁是当前实例对象
public synchronsized int isEmpty() {
//在此代码中访问共享数据
}
- 静态同步方法,锁是当前类的Class对象
一个线程试图访问同步代码时,首先必须得到锁,退出和抛异常时,必须释放锁,保证了同一时间只能有一个线程访问共享资源,从而保证了线程安全。
2、synchronized实现原理
- synchronzied基于jvm实现,具体来讲,是基于进入和退出Monitor对象实现同步,代码块同步是使用monitorenter、monitorexit指令实现,而方法同步是使用另一种方法实现的;
- monitorenter、和monitorexit在代码编译后被分别插入同步代码的开始位置和结束位置(异常位置也有),当执行到monitorenter时,将会尝试获取minitor的所有权,及对象的锁。
- 每一个对象都有一个monitor与之关联,当monitor被持有时,对象即被锁定;
下面通过示例代码看下同步代码快的实现:
//以下代码并无实际意义,只为说明问题
public class synchronizedTest {
public int getState(){
Object o = new Object();
synchronized (o) {
for(int i=0;i<1000;i++) {
System.out.println(i);
}
}
return 1;
}
}
使用javap反编译之后可以看到monitorenter、monitorexit两个指令(11行、35行、42行)
public class synchronizedTest {
public synchronizedTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int getState();
Code:
/...../
9: dup
10: astore_2
11: monitorenter
12: iconst_0
13: istore_3
/...../
31: goto 14
34: aload_2
35: monitorexit
36: goto 46
39: astore 4
41: aload_2
42: monitorexit
43: aload 4
45: athrow
/....../
}
事实上,只有在JDK1.6之前,synchronized的实现才会直接调用ObjectMonitor的enter和exit,这种锁被称之为重量级锁。为什么说这种方式操作锁很重呢?
- Java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就要从用户态转换到核心态,而这种上下文处理会花费很多的处理器时间,对于代码简单的同步块(如被synchronized修饰的get 或set方法)状态转换消耗的时间有可能比用户代码执行的时间还要长,所以说synchronized是java语言中一个重量级锁。
- JDK1.6之后 对锁做了很多优化,包括:偏向锁、轻量级锁等,后面会继续更新。
参考:
1、https://www.hollischuang.com/archives/2030
2、《并发编程的艺术》
欢迎扫码关注公众号共同升级打怪吖