1. 概述
在Java早期版本,synchronized
属于重量级锁,效率低下,因为使用监视器锁(monitor)依赖于底层操作系统的互斥锁(Mutex Lock)来实现,而Java的线程是映射到操作系统的原生线程之上的,如果挂起或者唤醒一个线程,都需要操作系统从用户态切换到内核态,这个状态之间的切换需要耗费较高的时间成本,这也是为什么早期的synchronized
效率低的原因。synchronized
使对象在同一时刻只能被一个线程访问,因此能够解决多线程之间数据同步问题。
2. 使用方式
- 同步普通方法:对当前对象加锁。
- 同步静态方法:对当前
Class
对象加锁。 - 同步代码块:对当前代码块加锁。
3. 实现原理
synchronized
同步语句块的实现使用JVM的是monitorenter
和monitorexit
指令,其中monitorenter指令指向同步代码块的开始位置, monitorexit指令指向同步代码块的结束位置。当执行monitorenter指令时,线程试图获取锁,也就是获取monitor的持有权。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一。
monitor对象存在于每个对象的对象头中,synchronized便是通过这种方式获取锁的,也就是为什么Java中任意对象可以作为锁的原因。
4. synchronized和ReentrantLock的区别
-
两者都是可重入锁
可重入锁,已经获得锁的线程可以再次获取自己的内部锁。比如一个线程获得了某个对象锁,此时这个对象锁还没有释放,当这个线程再次想要获取这个对象锁的时候还是可以获取的,如果锁不可重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。 -
两者依赖不同
synchronized
依赖于JVM
,而RennTrantLock
依赖于API
。JDK1.6为synchronized关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。RenntrantLock是JDK层面实现,也就是API层面,需要lock()
和unlock()
方法配合try/finally语句块来完成。
5. 使用示例
public class SynchronizedDemo {
//声明一个实例变量
private String name = "test";
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread("同步普通方法" + i) {
@Override
public void run() {
new SynchronizedDemo().demo1();
}
}.start();
new Thread("同步静态方法" + i) {
@Override
public void run() {
SynchronizedDemo.demo2();
}
}.start();
new Thread("同步当前对象" + i) {
@Override
public void run() {
new SynchronizedDemo().demo3();
}
}.start();
new Thread("同步指定对象" + i) {
@Override
public void run() {
new SynchronizedDemo().demo4();
}
}.start();
new Thread("同步class类对象" + i) {
@Override
public void run() {
new SynchronizedDemo().demo5();
}
}.start();
}
}
/**
* 同步实列方法
*/
public synchronized void demo1() {
try {
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 同步静态方法
*/
public synchronized static void demo2() {
try {
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 同步当前对象
*/
public void demo3() {
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 同步指定对象
*/
public void demo4() {
synchronized (name) {
try {
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 同步class类对象
*/
public void demo5() {
synchronized (SynchronizedDemo.class) {
try {
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
网友评论