synchronized 可以锁 String参数吗

synchronized锁对象的时候,保证同步代码块中的代码执行是串行执行的前提条件是锁住的对象是同一个。

在Java中是有常量池缓存的功能的,就是说如果我先声明了一个String str1 = “a”; 再声明一个一样的字符串的时候,取值是从原地址去取的,也就是说是同一个对象。这也就导致了在锁字符串对象的时候,可以会取得意料之外的结果(字符串一样会取得相同锁)

串行说明两个线程持有的是同一个锁,即字符串对象是同一个


https://blog.csdn.net/HeadingAlong/article/details/86505420

https://blog.csdn.net/u011277123/article/details/71719851

https://www.cnblogs.com/xrq730/p/6662232.html

String是值传递还是引用传递 参见:  https://www.jianshu.com/p/44d02f8db2eb


Integer 和 String 类型的对象在 JVM 里面是可能被重用的,除此之外,JVM 里可能被重用的对象还有 Boolean,那重用意味着什么呢?意味着你的锁可能被其他代码使用,如果其他代码 synchronized(你的锁),而且不释放,那你的程序就永远拿不到锁,这是隐藏的风险。

http://www.meilongkui.com/archives/56

错误地在基本类型上使用synchronize关键字是导致并发问题的常见原因之一,可能导致死锁或其他不可预测的结果,并将其抽象为不应该在任何可能被重用的对象[包括可能在JVM内部重用的对象]上使用synchronize关键字.

1、Synchronizes on a Boolean object(在Boolean类型的对象上使用了Synchronize关键字)

此种情况就是本文开头中FindBugs发现的问题,即Boolean类型的对象不可用于synchronization同步锁。其问题的实质在于Boolean.TRUE和Boolean.FALSE这两个常量在JVM中实际是java.lang.Boolean类的两个静态成员变量,因而可能在程序中被多处引用

例如,当例子中的inited指向Boolean.FALSE时,如果有其他的同步代码块在无意中使用了相同的Boolean常量,那么就有可能导致死锁

2、Synchronizes on a boxed primitive object(在装箱基本类型的对象上使用了Synchronize关键字)

上述代码将int类型的count自动装箱为Interger包装类型的对象Lock,然后使用synchronize关键字对包装类型的Lock变量加锁。可以预见,出于存储和性能等等的考虑,在自动装箱时JVM内部必然会重用具有相同值类型的包装类,因而Lock指向的对象极有可能被重用,进而在后续引发与Boolean类型变量存在的相同问题。

3、Synchronizes on a interned String lock object(在内部字符串对象上使用了Synchronize关键字)

根据Java API文档,intern()方法实际是返回对象池中的对象,因而调用intern()方法后获得到对象相当于JVM中的一个全局变量:

所以,即便是像上述错误代码中使用private和final关键字修饰lock变量,lock变量指向的仍然是同一个可能被重用的字符串常量。这种情况所带来的问题与之前提到的两种问题类似。

4、Synchronizes on a String Literal(在字符串常量上使用了Synchronize关键字)

基于之前的解释很容易理解第四种情况,即JVM中的字符串常量是全局重用的。

CERT规范给出了如下的几个应对方法

1、使用非装箱的Interger

当显式实例化Integer类型的变量时,相应的Integer变量会装箱相同的简单类型值但是建立唯一的引用,从而避免自动装箱导致的问题:

      虽然使用非装箱的Interger可以解决自动装箱类型导致的问题,但是由于程序员很难区分一个Integer到底是自动装箱的还是显式实例化的所以同样会导致维护性问题(因而最好使用private final类型的java.lang.Object,也就是下述的第三种解决方法)。

      这种情况实际上就是FindBugs中的“Synchronization on boxed primitive values”,FindBugs认为使用这种解决方法可能是正确的,但同样是令人困惑的而且可能会由于后续的重构[例如在IntelliJ中去除装箱时]而引发问题(The existing code might be OK, but it is confusing and a future refactoring, such as the “Remove Boxing” refactoring in IntelliJ, might replace this with the use of an interned Integer object shared throughout the JVM, leading to very confusing behavior and potential deadlock.)。

2、使用字符串实例

与字符串常量不同,字符串实例的引用是唯一的,因而不存在字符串常量可能导致的问题。

3、使用private final类型的java.lang.Object

CERT规范中提到,这种解决方法是少数可以直接利用到java.lang.Object的情况。在此处之所以强调使用Raw Object,同时还在CERT规范的原文中多次提到《Use private final lock objects to synchronize classes that may interact with untrusted code》,是由于不使用Raw Object可能导致被exploit的问题,在此不再赘述。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容