2017-05-07 1 views
0

스레드 안전성 여부를 이해하려고합니다. 나는 그렇다고 믿지만 누군가 최근에이 메서드의 스레드 안전성에 대해 의문을 제기했습니다.스레드 안전성 이해

의 내가 우리에게 다음과 같은 인터페이스를 구현하는 클래스주고 일부 공장 FactoryA 있다고 가정 해 봅시다 : 그래서

public abstract class MyFactory { 
    private ObjectA object; 
    public void init(ObjectA object){ 
     _object = object; 
    } 
} 

을, 우리는 이제

public class FactoryA extends MyFactory { 
    static final createFactoryClass(String name) { 
     // ...ignorning safety checks for brevity 
     return MY_MAP.get(name).newInstance(); 
    } 
} 

처럼, 나는 다른 몇 가지 방법을 가지고 뭔가있다 공장을 가지고 가능한 클래스지도를 돌려주는 클래스 :

public class OtherClass { 
    private static FactoryA _factory = new FactoryA(); 
    private static final Map<String, SomeClass> MY_MAP = new ImmutableMap.Builder<String, MyClass>() 
    .put("foo", _factory.createFactoryClass("foo")); 

    private SomeObject myMethod(ObjectA objectA, SomeObject someObject) { 
     MY_MAP.get(someObject.getString()).init(objectA); 
    } 
} 

질문은 init 메서드가 스레드로부터 안전한지 여부입니다. 지도는 한 번만 초기화되므로 불변 구조에 저장되어 있어도 두 스레드가 다른 ObjectA을 사용하여 호출하는 경우 잘못된 클래스가 잘못된 ObjectA을 사용할 수 있습니까?

다음과 같이이 문제를 해결할 수 있습니까? 이 예에서

static void setValue(Example obj, int val) { 
    obj.val = val; 
} 

Example sharedObj = ...; 

new Thread(() -> { setValue(sharedObj, 1); }).start(); 
new Thread(() -> { setValue(sharedObj, 2); }).start(); 

하지만, : 변수가 공유 객체, 즉 다음 예에서와 같은에 대한 참조가 아닌

private static synchronized myMethod(...) {}

+0

코드에서 사용자가 클래스 간 및 형식 불일치를 참조하기 때문에 약간 혼란 스럽습니다. 하지만 진짜 질문은'init' 메소드에서하는 일에 있다고 생각합니다. – Xerillio

+1

주어진 'MyFactory.init' 메소드는 아무 것도하지 않기 때문에 완전히 스레드 안전합니다. –

+0

'createFactoryClass()'도 스레드로부터 안전합니다. 지도를 초기화하면 다시 쓰지 않습니다. 여기 단서는 그것이 여전히 가시적이어야한다는 것이다; 그것을'final'에 할당하는 것은 괜찮 았고'휘발성'과'synchronized'와 같이 맵 (그리고 모든 쓰기)을 볼 수있게합니다. C.f. [ "Safe Publication"] (http://stackoverflow.com/questions/801993/java-multi-threading-safe-publication) – markspace

답변

1

지역 변수는 항상 스레드 안전 안전하지 않은 sharedObj 그 자체에 대한 참조를 사용하는 것이 아니며, 그 참조를 사용하여 sharedObj.val의 상태를 동시에 변경하는 것이 사실입니다.

Thread threadA = new Thread(() -> { 
    Example objA = ...; 
    setValue(objA, 1); 
}); 
Thread threadB = new Thread(() -> { 
    Example objB = ...; 
    setValue(objB, 2); 
}); 
threadA.start(); 
threadB.start(); 

JVM이 혼란스러워하고, 예를 들어 통과하지 않습니다

대신 경우 우리는 다른 개체에 대한 참조를 가진 두 개의 스레드를했다 threadA의 객체는 의 호출 인 setValue으로, 그 반대의 경우도 마찬가지입니다. 그것은 당신이 묻고있는 것처럼 들리는 것이고 그것은 일어나지 않을 것입니다. 모든 스레드는 자체 호출 스택을 가지고 있으며 각 스레드의 setValue 호출은 자체 스레드에서 새 스택 프레임을 엽니 다. 즉, threadAsetValue을 호출하여 threadA에 자체 로컬 변수가 있고 threadBsetValue을 호출하여 threadB에 자체 로컬 변수가있는 스택 프레임을 만듭니다.

init 변경 사항이 다른 스레드에 의해 표시되지 않을 수 있다는 별도의 우려 사항이 있습니다. 예를 들어, Thread thread1의 작업이 객체를 init에 전달하여 객체를 초기화 한 다음 다른 객체 인 Thread thread2으로 전달한다고 가정 해 보겠습니다. thread2init가 변경 한 내용을 thread1에 표시합니까? 대답은 아니요입니다. 일종의 메모리 동기화가 없으면 thread1 님이 init을 호출하는 동안 변경된 내용이 표시되지 않을 수 있습니다.

myMethod을 동기화해도 문제가 해결되지 않을 수도 있습니다.대신 thread1thread2은 공유 모니터 객체 나 잠금처럼 통신 할 수있는 방법이 필요합니다.

지도의 초기화에 대한 힌트를 얻은 세 번째 문제가 있습니다. 지도는 static 초기화 동안에 만 내장되어있는 경우 class initialization is performed under synchronization하고 있기 때문에, 그것은, 스레드 안전 :

구현은 락 취득을 eliding이 절차를 최적화 할 수 있습니다 [...]이 결정할 수있을 때 그 메모리 모델의 관점에서, 잠금을 획득 한 경우 존재할 수있는 순서가 최적화가 수행 될 때까지 여전히 존재한다면, 클래스의 초기화가 이미 완료된 것입니다. 즉

은 정적 초기화는 JVM 특정 클래스가 초기화 된 이후 잠시되었습니다 것을 결정하고 초기화를 취득하려고하지 않고 필드를 읽을하기로 결정하더라도, 다른 스레드에 의해 볼 수 보장 자물쇠.

-1

현재 어떤 일이 일어나고 있는지 이해하기 어렵 기 때문에 클래스/메소드에 대해 다른 이름을 사용하는 것이 좋습니다. 또한 나는 공장을 싱글 톤으로 만들 것이다.

import java.util.HashMap; 
import java.util.Map; 


public class FactoryStore { 

private static final Map<String, AbstractFactory> FACTORIES = new HashMap<>(); 

static { 
    FACTORIES.put("a", FactoryA.getInstance()); 
    FACTORIES.put("b", FactoryB.getInstance()); 
} 

public static Object createObject(String factoryName, Object parameters) { 
    return FACTORIES.get(factoryName).createNewObject(parameters); 
} 
} 



abstract class AbstractFactory { 

Object createNewObject(Object parameters) { 
    // not thread-safe stuff 
    return new Object(); 
} 


} 



class FactoryA extends AbstractFactory { 

private static final FactoryA instance = new FactoryA(); 

private FactoryA() { 
    // thread safe stuff 
} 

public static FactoryA getInstance() { 
    return instance; 
} 


} 


class FactoryB extends AbstractFactory { 

private static final FactoryB instance = new FactoryB(); 

private FactoryB() { 
    // thread safe stuff 
} 

public static FactoryB getInstance() { 
    return instance; 
} 

@Override 
synchronized Object createNewObject(Object obj) { 
    // can override object creation; this is thread-safe thanks to keyword 
    return new Object(); 
} 

}