继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

再有人问你synchronized是什么,就把这篇文章发给他。

慕村9548890
关注TA
已关注
手记 1302
粉丝 228
获赞 995

在《深入理解Java虚拟机》中,有这样一段话:

synchronized关键字在需要原子性、可见性和有序性这三种特性的时候都可以作为其中一种解决方案,看起来是“万能”的。的确,大部分并发控制操作都能使用synchronized来完成。

海明威在他的《午后之死》说过的:“冰山运动之雄伟壮观,是因为他只有八分之一在水面上。”

对于程序员来说,synchronized只是个关键字而已,用起来很简单。之所以我们可以在处理多线程问题时可以不用考虑太多,就是因为这个关键字帮我们屏蔽了很多细节。

那么,本文就围绕synchronized展开,主要介绍其用法、原理,以及如何提供原子性、可见性和有序性保障的等。

synchronized的用法

synchronized是Java提供的一个并发控制的关键字。主要有两种用法,分别是同步方法和同步代码块。

也就是说,synchronized既可以修饰方法也可以修饰代码块。代码如下:

/**

* @author Hollis 18/08/04.

*/

publicclassSynchronizedDemo{

//同步方法

publicsynchronizedvoiddoSth(){

System.out.println("Hello World");

}

//同步代码块

publicvoiddoSth1(){

synchronized (SynchronizedDemo.class){

System.out.println("Hello World");

}

}

}

被synchronized修饰的代码块及方法,在同一时间,只能被单个线程访问。

synchronized的实现原理

synchronized,是Java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问到时,我们可以在代码中使用synchronized关键字对类或者对象加锁。

深入理解多线程(一)——Synchronized的实现原理中我曾经介绍过其实现原理,为了保证知识的完整性,这里再简单介绍一下,详细的内容请去原文阅读。

我们对上面的代码进行反编译,可以得到如下代码:

publicsynchronizedvoiddoSth();

descriptor: ()V

flags: ACC_PUBLIC, ACC_SYNCHRONIZED

Code:

stack=2, locals=1, args_size=1

0: getstatic     #2// Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc           #3// String Hello World

5: invokevirtual #4// Method java/io/PrintStream.println:(Ljava/lang/String;)V

8:return

publicvoiddoSth1();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=2, locals=3, args_size=1

0: ldc           #5// class com/hollis/SynchronizedTest

2: dup

3: astore_1

4: monitorenter

5: getstatic     #2// Field java/lang/System.out:Ljava/io/PrintStream;

8: ldc           #3// String Hello World

10: invokevirtual #4// Method java/io/PrintStream.println:(Ljava/lang/String;)V

13: aload_1

14: monitorexit

15:goto23

18: astore_2

19: aload_1

20: monitorexit

21: aload_2

22: athrow

23:return

通过反编译后代码可以看出:

对于同步方法,JVM采用ACC_SYNCHRONIZED标记符来实现同步。

对于同步代码块。JVM采用monitorenter、monitorexit两个指令来实现同步。

在The Java® Virtual Machine Specification中有关于同步方法和同步代码块的实现原理的介绍,我翻译成中文如下:

方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。

同步代码块使用monitorenter和monitorexit两个指令实现。可以把执行monitorenter指令理解为加锁,执行monitorexit理解为释放锁。 每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程获得锁(执行monitorenter)后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。

无论是ACC_SYNCHRONIZED还是monitorenter、monitorexit都是基于Monitor实现的,在Java虚拟机(HotSpot)中,Monitor是基于C++实现的,由ObjectMonitor实现。



作者:Java填坑之路
链接:https://www.jianshu.com/p/e078eee057b7


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP

热门评论

哥们,发文章的时候先把代码格式化一下,,

查看全部评论