前言
在上一篇文章中我们讲到实现了一个数值序列生成器
但是在我们的多线程的环境下,会发现生成的数值序列不是我们所预期的那种递增的序列,而是会出现那种我们所不预期的错误
当时的解决方案比较简单,就在那个方法上加了一个synchronized,于是这个问题就解决了,我们并没有去详细的解释为什么在方法上加了一个synchronized就能够解决
从本篇文章开始,我们来正式的了解解决线程安全性问题的各种的方案,首先第一个就是synchronized,我们后面还会学习其他的锁呀、包括我们的元子类等等
一、内置锁
在看synchronized之前,我们首先来看内置锁,什么是内置锁呢
就是说Java中每一个对象都可以用作同步的锁,那么这些锁就被称之为内置锁
我们使用代码体会体会内置锁是什么意思?
public class Sequence {
int value;
public synchronized int getNext(){
return value++;
}
}
我们说每一个Java中的对象都是一个内置锁,当这个线程在进入同步代码块之前,都必须先获得锁也就是获得内置锁,然后获得锁之后就可以进入到同步代码块中执行
而我们的synchronized放在方法上,也就是说实例方法普通方法上,那么内置锁也就是所谓的对象锁,就是当前类的实例
public class Sequence {
/**
* synchronized 放在普通方法上,默认内置锁是当前实例
* @return
*/
public synchronized int getNext(){
return value++;
}
}
而其他的线程再想进来,那么就必须等待,于是这个线程开始往下执行,我们之前也都翻译过了,value++是执行了好多的字节码指令,那么,等字节码指令执行完毕之后,这个线程就可以释放掉锁了,把它拿到的锁释放掉
这个synchronized它除了能用在方法上以外,它还能用修饰静态方法,也就是类的方法
public class Sequence {
private static int value;
/**
* synchronized 放在静态方法上,内置锁是当前的Class字节码对象
* @return
*/
public static synchronized int getPrevious(){
return value --;
}
}
同时它除了修饰方法以外它还能够修饰代码块,我们可以举个例子使用同步代码块来修饰它
public class Sequence {
public int getValue(){
synchronized (Integer.valueOf(value)){
}
if(value>0){
return value;
}else{
return -1;
}
}
}
万物皆对象,你只要这里指定一个对象,那么,就可以作为一把锁
其实synchronized的原理其实就是加了锁,内置锁和互斥锁所决定的。每个对象都有锁,而这些锁都是互斥的,一个进来之后,另外的就不能进来了,因此就可以保证线程的安全性
二、字节码查看synchronized
那么在从字节码层面,在Java虚拟机规范中,可以看到,synchronized在Java虚拟机中执行同步代码块的时候,其实它是基于进入和退出monitor对象来实现方法同步的
我们可以观看字节码指令查看该Sequence.class的同步代码块部分
发现在第4行就是monitorenter进入执行,执行到第17行就退出
而当我们一般编写if-else时就会遇到goto,同时会跳到相应的操作并也释放锁
参考资料
龙果学院:并发编程原理与实战(叶子猿老师)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。