拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Java Synchronized同步不正确的用法

Java Synchronized同步不正确的用法

白鹭 - 2021-11-24 530 0 0

1.概述

Java中的同步对于摆脱多线程问题很有帮助。但是,如果不仔细使用同步原理,可能会给我们带来很多麻烦。

在本教程中,我们将讨论与同步相关的一些不良做法以及每种用例的更好方法。

2.同步原理

通常,我们应该仅在确定没有外部代码锁定的对像上进行同步

换句话说,使用池化或可重用的对象进行同步是一种不好的做法。原因是JVM中的其他进程可以访问池化/可重用的对象,并且外部/不受信任的代码对此类对象进行的任何修改都可能导致死锁和不确定性行为。

现在,让我们讨论基于某些类型的同步原理,例如StringBooleanIntegerObject

3. String字符串

3.1 不良做法

字符串文字被合并,并经常在Java中重用。因此,建议不要将String类型与synchronized关键字一起使用来进行同步:

public void stringBadPractice1() {

 String stringLock = "LOCK_STRING";

 synchronized (stringLock) {

 // ...

 }

 }

同样,如果我们使用private final String文字,则仍从常量池中引用它:

private final String stringLock = "LOCK_STRING";

 public void stringBadPractice2() {

 synchronized (stringLock) {

 // ...

 }

 }

此外,它被认为是不好的做法, internString进行同步:

private final String internedStringLock = new String("LOCK_STRING").intern();

 public void stringBadPractice3() {

 synchronized (internedStringLock) {

 // ...

 }

 }

根据Javadocsintern方法为我们提供String对象的规范表示。换句话说, intern方法从池中返回一个String ,并将其显式添加到池中(如果不存在),其内容与此String相同。

因此,对于可重复使用的对象,同步问题对于持久化的String像也仍然存在。

注意:所有String文字和字符串值常量表达式都将自动被interned

3.2 解决办法

建议避免对String文字进行同步的不良做法,建议使用new关键字创建String的新实例

让我们用已经讨论过的代码解决问题。首先,我们将创建一个新的String对象,使其具有唯一的引用(以避免任何重用)和它自己的固有锁定,这有助于同步。

然后,我们将对象保持privatefinal以防止任何外部/不受信任的代码访问它:

private final String stringLock = new String("LOCK_STRING");

 public void stringSolution() {

 synchronized (stringLock) {

 // ...

 }

 }

4. Boolean类型

具有两个值truefalseBoolean类型不适用于锁定目的。类似于JVM中的String文字, boolean文字值还共享Boolean类的唯一实例。

让我们看一个错误的代码示例,该示例在Boolean锁定对像上进行同步:

private final Boolean booleanLock = Boolean.FALSE;

 public void booleanBadPractice() {

 synchronized (booleanLock) {

 // ...

 }

 }

在这里,如果任何外部代码也在具有相同值的Boolean文字上同步,则系统可能变得无响应或导致死锁情况。

因此,我们不建议将Boolean对像用作同步锁。

5.装箱基本类型

5.1。不良做法

类似于boolean文字,装箱的类型可能会将实例重用于某些值。原因是JVM缓存并共享可以表示为字节的值。

例如,让我们写一个糟糕的代码示例,对盒装类型Integer同步:

private int count = 0;

 private final Integer intLock = count;

 public void boxedPrimitiveBadPractice() {

 synchronized (intLock) {

 count++;

 // ...

 }

 }

5.2 解决办法

但是,与boolean文字不同,对盒装原语进行同步的解决方案是创建一个新实例。

String像类似,我们应该使用new关键字创建具有自己的内部锁的Integer对象的唯一实例,并将其保持privatefinal

private int count = 0;

 private final Integer intLock = new Integer(count);

 public void boxedPrimitiveSolution() {

 synchronized (intLock) {

 count++;

 // ...

 }

 }

6.this同步

当类使用this关键字实现方法同步或阻止同步时,JVM将对象本身用作监视器(其内部锁)。

不受信任的代码可以获取并无限期持有可访问类的内部锁。因此,这可能导致死锁情况。

6.1。不良做法

例如,让我们使用synchronized方法setName和带有synchronized块的方法setOwner创建Animal类:

public class Animal {

 private String name;

 private String owner;



 // getters and constructors



 public synchronized void setName(String name) {

 this.name = name;

 }



 public void setOwner(String owner) {

 synchronized (this) {

 this.owner = owner;

 }

 }

 }

现在,让我们编写一些错误的代码来创建Animal类的实例并对其进行同步:

Animal animalObj = new Animal("Tommy", "John");

 synchronized (animalObj) {

 while(true) {

 Thread.sleep(Integer.MAX_VALUE);

 }

 }

在这里,不受信任的代码示例引入了不确定的延迟,从而阻止了setNamesetOwner方法的实现获取相同的锁。

6.2 解决办法

防止此漏洞的解决方案是私有锁对象

这个想法是使用与在我们的类中定义的Object的**private final实例相关联的内在锁来代替对象本身的内在锁**。

另外,我们应该使用块同步来代替方法同步,以增加灵活性,以将异步代码排除在块外。

因此,让我们对Animal类进行必要的更改:

public class Animal {

 // ...



 private final Object objLock1 = new Object();

 private final Object objLock2 = new Object();



 public void setName(String name) {

 synchronized (objLock1) {

 this.name = name;

 }

 }



 public void setOwner(String owner) {

 synchronized (objLock2) {

 this.owner = owner;

 }

 }

 }

在这里,为了更好的并发性,我们通过定义多个private final锁定对象来细化锁定方案,以分离我们对两个方法setNamesetOwner同步关注。

此外,如果实现synchronized块的方法修改了static变量,则必须通过锁定static对象来进行同步:

private static int staticCount = 0;

 private static final Object staticObjLock = new Object();

 public void staticVariableSolution() {

 synchronized (staticObjLock) {

 count++;

 // ...

 }

 }

7.结论

在本文中,我们讨论了与某些类型(如StringBooleanIntegerObject上的同步相关的一些不良做法。

本文最重要的要点是,不建议使用池化或可重用的对象进行同步。

另外,建议在Object类的private final实例上进行同步。外部/不受信任的代码将无法访问此对象,否则这些代码可能会与我们的public类进行交互,从而减少了此类交互可能导致死锁的可能性。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *