Do not reassign object references to locked objects

zhaozj2021-02-08  305

Synchronized keyword lock object. The object is locked inside the Synchronized code, which does this object and what you have made by your object reference? Synchronous processing for an object is only locked. However, you must be aware that don't reassign object references to the locked object. So what happens if this happens? Consider the following code, it implements a Stack:

Class Stack

{

PRIVATE INT stacksize = 10;

Private int [] INTARR = New Int [stacksize];

Private int index; // STACK in the next available location.

Public void push (int val)

{

Synchronized (INTARR) {

// If it is full, you will reassign an integer array (ie our stack).

IF (index == INTARR.LENGTH)

{

Stacksize * = 2;

int [] newinTarr == new int [stacksize];

System.Arraycopy (INTARR, 0, NewIntarr, 0, INTARR.LENGTH);

INTARR = NewIntarr;

}

INTARR [INDEX] == VAL;

INDEX ;

}

}

Public int Pop ()

{

int Retval;

Synchronized (INTARR) {

IF (INDEX> 0)

{

Retval = INTARR [index-1]; // Review value,

Index -; / / and reducing Stack by 1 value.

Return RetVal;

}

}

Throw new emptystackexception ();

}

// ...

}

This code implements a Stack with an array. An array of initial size is created to accommodate an integer value. This type implements the PUSH and POP methods to simulate the use of stack. In the PUSH method, if there is no more space in the array to accommodate the crimped value, the array is reassigned to create more storage space. (Instead of using the vector to implement this class. The basic type cannot be stored in theVector.)

Note that this code is to be accessed by multiple threads. The PUSH and POP methods are completed within the Synchronized block each time the access of the shared instance data of the class is completed. This ensures that multiple threads cannot be concurrently accessing this array and generate incorrect results.

This code has a major disadvantage. It works synchronously for integer array objects, and this array is referenced by the Stack class's INTARR. This disadvantage is revealed when the Push method is reassigned this integer array. When this happens, the object reference INTARR is reset to reference a new, larger integer array object. Note that this is happening during the execution of the SYNCHRONIZED block of the PUSH method. This block is synchronously processed for the object referenced by the INTARR variable. Therefore, the objects that are locked in this code are no longer used. Consider the following event sequences:

Thread 1 calls the PUSH method and obtain the lock of the INTARR object. Thread 1 is first robbed. Thread 2 calls the POP method. This method is blocked by the same lock held by the current thread 1 in the PUSH method. Thread 1 Re-control and reassign an array. The INTARR variable now references a different variable. The Push method exits and releases its lock to the original INTARR object. Thread 1 calls the PUSH method again and obtains the lock of the new INTARR object. Thread 1 is first robbed. Thread 2 obtains an object lock of the old INTARR object and attempts to access its memory. Now thread 1 holds the lock of the new object referenced by INTARR, thread 2 holds the lock of the old object referenced by INTARR. Because two threads hold different locks, they can perform SYNCHRONIZED PUSH and POP methods and cause errors. Obviously, this is not the desired result.

This problem is caused by reassign the object reference to the locked object by the PUSH method. When an object is locked, other threads may be blocked in the same object lock. If the object reference to the locked object is reassigned to another object, the hanging lock of other threads is an object that is no longer associated with the code.

You can correct this code to remove synchronization of the INTARR variable, and synchronize the PUSH and POP methods. This can be achieved by adding the synchronized keyword as a method modifier. The correct code is as follows:

Class Stack

{

// The same as the previous ...

Public synchronized void push (int val)

{

// If you are empty, you will redistribute an array (ie our stack).

IF (index == INTARR.LENGTH)

{

Stacksize * = 2;

Int [] newinTarr = new int [stacksize];

System.Arraycopy (INTARR, 0, NewIntarr, 0, INTARR.LENGTH);

INTARR = NewIntarr;

}

INTARR [INDEX] = VAL;

INDEX ;

}

Public Synchronized Int Pop ()

{

int Retval;

IF (INDEX> 0)

{

Retval = INTARR [INDEX-1];

Index -;

Return RetVal;

}

Throw new emptystackexception ();

}

}

This modification changed the actually acquired lock. The acquired lock is an object for the object to which it is called, not the object being referenced by the INTARR variable. Because the acquired lock is no longer referenced by INTARR, the code is allowed to retest the INTARR object reference.

转载请注明原文地址:https://www.9cbs.com/read-827.html

New Post(0)