Java的并发编程:从synchronized保证线程安全的原理
在Java的并发编程:线程的安全性问题的分析这篇文章中说到了synchronized可以保证线程的安全性,那么,这篇文章主要是说synchronized保证线程安全的原理,为什么加上synchronized就能保证线程的安全呢?我们可以从两个角度出发去看这个问题,一个是从理论的层面,一个是从JVM的层面。
从理论的层面看synchronized保证线程安全的原理
synchronized的原理有两个:
- 内置锁
- 互斥锁
Java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。而Java的内置锁又是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
synchronized保证线程的安全访问,那么,这个synchronized的关键字可以修饰什么呢?synchronized主要是用来修饰下面三个内容:
- 修饰普通方法
- 修饰静态方法
- 修饰代码块
synchronized修饰这个三个方法的Java示例:
package com.breakyizhan.thread.t3;
public class Sequence {
private int value;
/**
* synchronized 放在普通方法上,内置锁就是当前类的实例
* @return
*/
public synchronized int getNext() {
return value ++;
}
/**
* 修饰静态方法,内置锁是当前的Class字节码对象
* Sequence.class
* @return
*/
public static synchronized int getPrevious() {
// return value --;
return 0;
}
public int xx () {
// monitorenter
synchronized (Sequence.class) {
if(value > 0) {
return value;
} else {
return -1;
}
}
// monitorexit
}
public static void main(String[] args) {
Sequence s = new Sequence();
// while(true) {
// System.out.println(s.getNext());
// }
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
从JVM的层面看synchronized保证线程安全的原理
我们已经知道,在Java的并发编程:线程的安全性问题的分析这篇文章中说到,用java -verbose可以查看JVM虚拟机的二进制代码,那么,我们就来看一下synchronized修饰同步代码块的时候运行的机制,(修饰普通方法和修饰静态方法暂时看不到锁的获取和释放),在synchronized修饰同步代码块的时候 monitorenter和monitorexit代表了锁的获取和释放,如下图,代表着方法int xx():

简单说一下上面那个图,这个 monitorenter和monitorexit代表了锁的获取和释放,而方法int xx()并不是按顺序执行的,如果遇到第9步ifle也会跳到第19步,执行完之后再执行monitorexit释放锁,所以从java的字节码指令可以看到也是有很多monitorexit这样的命令,也是不奇怪的。
浙公网安备 33010602011771号