레코드 집합 (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()
명확성을 위해 예제가 포함 됩니다.
답변
나는 이것을 발견 Thread.holdsLock(Object obj)
했고 , 우리 는 객체가 잠겨 있는지 확인하는 데 사용할 수 있습니다 .
true
현재 스레드가 지정된 개체에 대한 모니터 잠금을 보유하는 경우에만 반환 합니다.
참고 Thread.holdsLock()
반환 false
잠금이 보유하면 뭔가 및 호출 스레드가 잠금을 수용하는 스레드가 아닙니다.
답변
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
}
}
내 조언은 java.util.concurrent Lock 객체를 사용하도록 코드를 리팩터링 할 수없고 Oracle VM에서 실행중인 경우에만이 접근 방식을 사용하는 것입니다.
답변
Lock 답변은 매우 좋지만 다른 데이터 구조를 사용하는 대안을 게시 할 것이라고 생각했습니다. 기본적으로 다양한 스레드는 어떤 레코드가 잠겨 있고 어떤 레코드가 잠겨 있는지 알고 싶어합니다. 이를 수행하는 한 가지 방법은 잠긴 레코드를 추적하고 데이터 구조에 잠긴 세트에 레코드를 추가하기위한 올바른 원자 적 작업이 있는지 확인하는 것입니다.
예를 들어 CopyOnWriteArrayList를 사용하겠습니다. 예를 들면 “마법”이 덜하기 때문입니다. CopyOnWriteArraySet이 더 적절한 구조입니다. 평균적으로 동시에 많은 레코드가 잠긴 경우 이러한 구현에 성능 영향이있을 수 있습니다. 적절하게 동기화 된 HashSet도 작동하고 잠금이 짧습니다.
기본적으로 사용 코드는 다음과 같습니다.
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);
}
레코드 당 잠금을 관리 할 필요가 없으며 어떤 이유로 든 모든 잠금을 해제해야하는 경우 단일 위치를 제공합니다. 반면에 레코드가 몇 개 이상인 경우 추가 / 제거 조회가 선형이 아닌 O (1)이기 때문에 동기화 된 실제 HashSet이 더 좋을 수 있습니다.
사물을 보는 다른 방식입니다. 실제 스레딩 요구 사항에 따라 다릅니다. 개인적으로 저는 Collections.synchronizedSet (new HashSet ())을 사용합니다. 정말 빠르기 때문입니다.
답변
또 다른 해결 방법은 (여기에 제공된 답변에 대한 기회가없는 경우) 시간 초과를 사용하는 것입니다. 즉, 하나 아래는 1 초간 중단 된 후 null을 반환합니다.
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;
}
답변
덕분에 경쟁 조건을 해결하는 데 도움이되었습니다. 벨트와 멜빵을 모두 착용하기 위해 조금 변경했습니다.
그래서 여기에 받아 들여지는 대답의 개선에 대한 제안이 있습니다.
tryLock()
다음과 같은 작업을 수행 하여 메서드에 안전하게 액세스 할 수 있습니다 .
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!");
}
}
이렇게 tryLock()
하면 거의 동시에 두 번의 전화 를 받을 수있는 상황을 피할 수 있어 수익이 의심 스러울 수 있습니다. 지금 내가 틀렸다면 여기서 조심 스러울지도 몰라요. 하지만 헤이! 내 공연은 이제 안정적입니다 🙂 ..
내 블로그 에서 내 개발 문제에 대해 자세히 읽어보십시오 .