code

객체가 잠겼는지 (동기화 됨) Java에서 차단되지 않도록 어떻게 결정합니까?

codestyles 2020. 11. 8. 10:02
반응형

객체가 잠겼는지 (동기화 됨) Java에서 차단되지 않도록 어떻게 결정합니까?


레코드 집합 (recordA, recordB 등)이있는 메모리에 테이블이 포함 된 프로세스 A가 있습니다.

이제이 프로세스는 레코드에 영향을주는 많은 스레드를 시작할 수 있으며 때로는 동일한 레코드에 액세스하려는 두 개의 스레드를 가질 수 있습니다.이 상황은 거부되어야합니다. 특히 레코드가 한 스레드에 의해 잠긴 경우 다른 스레드가 중단되기를 원합니다 (BLOCK 또는 WAIT를 원하지 않음).

현재 나는 다음과 같이한다.

synchronized(record)
{
performOperation(record);
}

그러나 이것은 나에게 문제를 일으키고 있습니다 ... 왜냐하면 Process1이 작업을 수행하는 동안 Process2가 들어 오면 동기화 된 문을 차단 / 대기하고 Process1이 완료되면 작업을 수행하기 때문입니다. 대신 다음과 같은 것을 원합니다.

if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}

이것이 어떻게 이루어질 수 있는지에 대한 단서가 있습니까? 어떤 도움이라도 대단히 감사하겠습니다. 감사,


한 가지 주목할 점은 그러한 정보를받는 즉시 부실하다는 것입니다. 즉, 아무도 잠금을 가지고 있지 않다고 들었을 때 획득하려고 할 때 다른 스레드가 수표와 획득을 시도하는 사이에 잠금을 해제했기 때문에 차단됩니다.

브라이언은를 지적하는 것이 옳지 Lock만, 당신이 정말로 원하는 것은 그 tryLock방법 이라고 생각합니다 .

Lock lock = new ReentrantLock();
......
if (lock.tryLock())
{
    // Got the lock
    try
    {
        // Process record
    }
    finally
    {
        // Make sure to unlock so that we don't cause a deadlock
        lock.unlock();
    }
}
else
{
    // Someone else had the lock, abort
}

tryLock대기 시간을두고 통화 할 수도 있습니다. 따라서 10 분의 1 초 동안 획득을 시도한 다음 얻을 수없는 경우 중단 할 수 있습니다 (예 :

(내가 아는 한 Java API가 Monitor클래스가 .NET에서 하는 것처럼 "내장"잠금에 대해 동일한 기능을 제공하지 않는다는 것이 유감이라고 생각합니다 . 스레딩과 관련하여 두 플랫폼 모두에서 내가 싫어하는 다른 것-예를 들어 잠재적으로 모니터가있는 모든 객체!)


Java 5 동시성 패키지에 도입 된 Lock 객체를 살펴보십시오 .

예 :

Lock lock = new ReentrantLock()
if (lock.tryLock()) {
   try {
      // do stuff using the lock...
   }
   finally {
      lock.unlock();
   }
}
   ...

ReentrantLock와의 객체는 기본적으로 기존과 동일한 일을하고 synchronized메커니즘,하지만 더 많은 기능과 함께.

편집 : Jon이 언급 했듯이이 isLocked()방법은 그 순간에 당신에게 알려주고 그 이후에는 그 정보가 오래되었습니다. 의 tryLock () 메소드는 더 안정적인 작동 (당신은 시간 제한이 사용뿐만 아니라 수 있습니다)을 줄 것이다

편집 # 2 : 이제 tryLock()/unlock()명확성을 위해 예제가 포함 됩니다.


Lock 객체를 사용하는 위의 접근 방식이 가장 좋은 방법이지만 모니터를 사용하여 잠금을 확인할 수 있어야한다면 그렇게 할 수 있습니다. 그러나이 기술은 Oracle Java가 아닌 VM으로 이식 할 수 없으며 지원되는 공용 API가 아니므로 향후 VM 버전에서 중단 될 수 있으므로 상태 경고가 표시됩니다.

방법은 다음과 같습니다.

private static sun.misc.Unsafe getUnsafe() {
    try {
        Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public void doSomething() {
  Object record = new Object();
  sun.misc.Unsafe unsafe = getUnsafe(); 
  if (unsafe.tryMonitorEnter(record)) {
    try {
      // record is locked - perform operations on it
    } finally {
      unsafe.monitorExit(record);
    }
  } else {
      // could not lock record
  }
}

My advice would be to use this approach only if you cannot refactor your code to use java.util.concurrent Lock objects for this and if you are running on an Oracle VM.


I found this, we can use Thread.holdsLock(Object obj) to check if an object is locked:

Returns true if and only if the current thread holds the monitor lock on the specified object.

Note that Thread.holdsLock() returns false if the lock is held by something and the calling thread isn't the thread that holds the lock.


While the Lock answers are very good, I thought I'd post an alternative using a different data structure. Essentially, your various threads want to know which records are locked and which aren't. One way to do this is to keep track of the locked records and make sure that data structure has the right atomic operations for adding records to the locked set.

I will use CopyOnWriteArrayList as an example because it's less "magic" for illustration. CopyOnWriteArraySet is a more appropriate structure. If you have lots and lots of records locked at the same time on average then there may be performance implications with these implementations. A properly synchronized HashSet would work too and locks are brief.

Basically, usage code would look like this:

CopyOnWriteArrayList<Record> lockedRecords = ....
...
if (!lockedRecords.addIfAbsent(record))
    return; // didn't get the lock, record is already locked

try {
    // Do the record stuff
}        
finally {
    lockedRecords.remove(record);
}

It keeps you from having to manage a lock per record and provides a single place should clearing all locks be necessary for some reason. On the other hand, if you ever have more than a handful of records then a real HashSet with synchronization may do better since the add/remove look-ups will be O(1) instead of linear.

Just a different way of looking at things. Just depends on what your actual threading requirements are. Personally, I would use a Collections.synchronizedSet( new HashSet() ) because it will be really fast... the only implication is that threads may yield when they otherwise wouldn't have.


Another workaround is (in case of you didnt have chance with the answers given here )is using timeouts. i.e. below one will return null after 1 second hanging:

ExecutorService executor = Executors.newSingleThreadExecutor();
        //create a callable for the thread
        Future<String> futureTask = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return myObject.getSomething();
            }
        });

        try {
            return futureTask.get(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            //object is already locked check exception type
            return null;
        }

Thanks for this, it helped me out solving a race condition. I changed it a little to wear both belt and suspenders.

So here is my suggestion for AN IMPROVEMENT of the accepted answer:

You can ensure that you get safe access to the tryLock() method by doing something like this:

  Lock localLock = new ReentrantLock();

  private void threadSafeCall() {
    boolean isUnlocked = false;

    synchronized(localLock) {
      isUnlocked = localLock.tryLock();
    }

    if (isUnlocked) {
      try {
        rawCall();
      }
      finally {
        localLock.unlock();
      }
    } else {
      LOGGER.log(Level.INFO, "THANKS! - SAVED FROM DOUBLE CALL!");
    }
  }

This would avoid the situation where you might get two calling tryLock() at the almost same time, causing the return to be potentially doubt full. I'd like to now if I'm wrong, I might be over cautios here. But hey! My gig is stable now :-)..

Read more on my development issues at my Blog.

참고URL : https://stackoverflow.com/questions/1779795/how-do-determine-if-an-object-is-locked-synchronized-so-not-to-block-in-java

반응형