Problem with Java synchronized sets when executing equals () in reverse order from multiple threads

Example script :

  • Create two Synchronized Sets (s1 and s2)
  • Transfer them to two streams (T1 and T2)
  • Start threads

T1 run (): while (forever) s1.equals (c2)

T2 run (): while (forever) s2.equals (s1)

What's happening? - SynchronizedSet gets lock on itself equally

  • It calculates the length of the passed parameter, as well as what it contains, to determine if it is [Note: this is an assumption based on the logs being analyzed]

  • If the passed in param is also a SynchronizedSet, calls to size () and containsAll () imply that a lock must also be obtained.

  • In the above example, the blocking of capture orders for T1 and T2 is as follows:

    T1: s1 → s2 T2: s2 → s1

Ofc, this leads to a deadlock.

This issue is not specific to synchronous collections only. This can even happen with a Hashtable or Vector.

I believe this is a limitation of the Java API (design). How to overcome this? How can I make sure this does not happen in my application? Is there some design principle that I must follow without getting into this situation?

+3
source share
5 answers

I believe this is a limitation of the Java API (design).

, . PL, - , , . .

, , , - :

  • , , ,
  • .

.

?

, . @Maurice @Nettogrof , , , , .

+4

:

            synchronized(s1) {
                synchronized(s2) {
                    s1.equals(s2);
                }
            }

            synchronized(s1) {
                synchronized(s2) {
                    s2.equals(s1);
                }
            }
+2

synchronized() {].

- :

while(forever){
    synchronized(s1){
        s1.equals(s2);
    }
}

while(forever){
   synchronized(s1){
    s2.equals(s1);
   }
}
+2

- . : , , "" , :

 private static final Object lock = new Object(); // May be context-local.

 [...]

     synchronized (lock) {
         synchronized (s1) {
             synchronized (s2) {
                 return s1.equals(s2);
             }
          }
     }

, , , - :

    int h1 = System.identityHashCode(s1);
    int h2 = System.identityHashCode(s2);
    return
         h1<h2 ? lockFirstEquals(h1, h2) :
         h2<h1 ? lockFirstEquals(h2, h1) :
         globalLockEquals(h1, h2);

. IIRC, StringBuffer ( ). :

public StringBuffer append(StringBuffer other) {
    if (other == null) {
        return append("null");
    }
    int thisHash  = System.identityHashCode(this);
    int otherHash = System.identityHashCode(other);
    if (thisHash < otherHash) {
        synchronized (this) {
            synchronized (other) {
                appendImpl(other);
            }
        }
    } else if (otherHash < thisHash) {
        synchronized (other) {
            synchronized (this) {
                appendImpl(other);
            }
        }
    } else {
        append(other.toString()); // Or append((Object)other);
    }
    return this;
}

, , , .

+1

You can order sets identiyHashCode()before making a call equals(). Thus, the order in which the lock is detected will always be the same.

+1
source

Source: https://habr.com/ru/post/1719862/


All Articles