欢迎大家关注我的公众号,会不定期更新一些开发与测试的一些技术文章。
synchronized基础特点:
synchronized锁是可重入的,且在父子类继承中同样适用;
synchronized锁在遇到异常时自动释放锁;
synchronized锁的同步化不可以继承;
1、非线程安全的高发区
1.1、 多线程下的全局变量
1.2、多线程下的未同步的方法
1.3、多线程下的未进行合理同步的方法
本篇主要介绍synchronized的使用技巧,主要针对的情况是1.2和1.3。
要点:只有共享资源的读写(比如java的读写锁,仅读读不需要独占同步,读写,写写,写读均需要独占式同步)操作才需要同步化,如果不是共享资源,则不需要进行多余的同步,这也涉及到锁的粒度问题,后面我再细聊。
synchronized是java中使用的较多的同步关键字,且一直被不断优化。使用synchronized较为简单,不像Lock那样需要手动释放锁,虽然失去了一定的灵活性,但对大多数情况下,synchronized还是明智的选择。下文会着重介绍synchronized的使用技巧。
2、synchronized修饰对象方法
2.1、当多个线程同时访问同一个对象的同步方法时,是线程安全的,注意一定是同一个对象,如果创建下图中的两个MyObject类并分别在两个线程的run方法中调用methodA方法,就是非线程安全的了,因为synchronized关键字获取的为对象锁。
如下图所示:
2.2、在同一个实例对象中,synchronized关键字仅对加了synchronized关键字的方法)会保证线程安全,而对同一个实例对象中的未加synchronized关键字方法不保证线程安全。
如下图所示:
注:在多线程情况下,同一个MyObject对象的methodA方法是线程安全的,而methodB则是非线程安全的。
2.3、在同一个实例对象中,如果多个方法都被synchronized关键字修饰,则该实例对象中的多个方法之间是同步的,需要等待上一个方法执行结束后下一个获得该对象锁的方法才可以执行,这也正说明了synchronized关键字获得是对象锁,多个方法会争夺同一个对象锁。
如下图所示:
注:方法A和方法B的执行是有序的,先获得对象锁的方法先执行,另一个方法会在该方法执行完毕再执行。
3、synchronized修饰静态方法
synchronized修饰静态方法时,持有的锁为类锁,在这种情况下,无论创建多少对象,都共同持有同一个锁即类锁。
如下图所示:
3.1、两个静态方法同时加synchronized时,会发生阻塞。
如下图所示:
3.2、一个静态方法一个动态方法,同时加synchronized,不会发生阻塞,原因是一个是静态方法的synchronized持有的是类锁,动态方法持有的synchronized是对象锁。
如下图所示:
3.3、同步synchronized(class)和直接使用synchronized修饰静态方法效果一样,都是获得类锁,而不是对象锁。
如下图所示:
4、synchronized结合静态内部类
4.1、synchronized(this)的情况下
当多个并发线程访问同一个对象的synchronized(this)同步代码块时,各个线程是依次执行的,即该对象的同步代码块中的部分是线程同步的。synchronized同步代码块提升一定的效率。
如下图所示:
结合上图中的代码,当一个线程访问该对象的synchronized同步代码块时,其他线程仍然可以访问该对象非synchronized同步代码块中的内容。且能符合我们对线程同步的预期,即synchronized同步代码块持有当前调用对象的锁,且为同步的。
注:当一个线程访问该对象的一个synchronized(this)同步代码块时,其他线程对该对象所有其他的synchronized(this)同步代码块的访问将被阻塞,因为synchronized(this)中的this获取的是整个对象的对象锁。
如下图所示:
方法1和方法2会依次执行。同理,下图中的三个方法也是依次执行的:方法3为synchronized修饰的整个方法,而方法1和方法2为synchronized(this)同步代码块。
4.2、synchronized(非this)的情况下
使用synchronized(非this)同步代码块时,同一个实例对象可以有多个synchronized(非this)同步代码块,当synchronized(非this)中非this对象相同时,多个synchronized(非this)代码块中的代码具有阻塞性,当非this对象不同时,多个synchronized(非this)代码块分别具有同步特性,相互之间不会出现阻塞。
如下图所示:
下图中两个synchronized块持有不同的锁,故不会发生阻塞,方法A持有anString锁,方法B持有anString2锁;
下图中第一个方法持有该对象的对象锁,第二个方法持有anString锁;
注:
4.3、当多个线程同时执行synchronized(x){}同步代码块时,呈同步效果,各个synchronized(x){}块会发生阻塞;
如下图所示:
4.4、当其他线程执行x对象中的synchronized同步方法或synchronized(this)同步代码块时,呈同步效果。
如下三个图所示:
<
4.5、如果其他线程调用未加synchronized的方法时,不会发生同步。
注:字符串一般不用于synchronized的锁对象,因为string常量池会产生出乎意料的锁同步和阻塞现象。另外如果锁对象放入缓存或者map后,如果被另一个synchronized所持有,也会发生同步或者阻塞。
5 、synchronized结合内部类
本质上和上述一致,只是将判断锁对象以及使用synchronized位置变换了下而已,只要找到正确的锁对象以及共享数据,就可以轻易的对同步是否合理及是否需要改进进行判断。
参考:java多线程核心编程技术
欢迎大家关注我的公众号,会不定期更新一些开发与测试的一些技术文章。
本篇完。持续更新完善中......关注有惊喜,共同学习