예를 들어 동기화 된 블록보다 동기화 된 방법의 장점을 알려주는 사람이 있습니까?
예를 들어 동기화 된 블록보다 동기화 된 방법의 장점을 알려주는 사람이 있습니까?
답변:
누구든지 예를 들어 동기화 된 블록보다 동기화 된 방법의 장점을 말해 줄 수 있습니까? 감사.
블록보다 동기화 된 방법을 사용하면 확실한 이점이 없습니다.
아마도 유일한 것은 (그러나 나는 그것을 이점이라고 부르지 않을 것입니다) 당신은 객체 참조를 포함 할 필요가 없다는 것 this
입니다.
방법:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
블록:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
보다? 전혀 이점이 없습니다.
블록 은 다른 객체를 잠금으로 사용할 수 있지만 메서드를 동기화하면 전체 객체가 잠길 수 있기 때문에 대부분의 유연성에서 블록 이 메서드보다 장점이 있습니다.
비교:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
vs.
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
또한 방법이 커지면 동기화 된 섹션을 계속 분리 할 수 있습니다.
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronized
블록은 두 개의 명령어를 이용하여 구현되며, monitorenter
그리고 monitorexit
, 플러스 것을 보장 예외 핸들러 monitorexit
에도 특별한 경우에 호출된다. synchronized
메소드를 사용할 때 모두 저장됩니다 .
유일한 차이점은 동기화 된 블록이 동기화 할 객체를 선택할 수 있다는 것입니다. 동기화 된 메소드는 'this'
(또는 동기화 된 클래스 메소드의 해당 클래스 인스턴스) 만 사용할 수 있습니다 . 예를 들어 다음은 의미 상 동일합니다.
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
후자는 모든 객체, 종종 멤버 변수 의 관련 잠금을 위해 경쟁 할 수 있기 때문에 더 유연 합니다. 또한 블록 전후에 동시에 메서드 내에서 동시 코드를 실행할 수 있기 때문에 더 세분화됩니다. 물론 동시 코드를 별도의 비 동기화 메소드로 리팩토링하여 동기화 된 메소드를 쉽게 사용할 수 있습니다. 코드를 이해하기 쉽게 만드는 방법을 사용하십시오.
장점 :
단점 :
장점 :
단점 :
개인적으로 동기화가 필요한 것에 중점을 둔 클래스와 동기화 된 메소드를 사용하는 것을 선호합니다. 이러한 클래스는 가능한 한 작아야하므로 동기화를 쉽게 검토 할 수 있어야합니다. 다른 사람들은 동기화에 신경 쓸 필요가 없습니다.
주요 차이점은 동기화 된 블록을 사용하는 경우이 이외의 다른 객체를 잠그면 훨씬 더 유연해질 수 있다는 것입니다.
메시지 대기열과 여러 메시지 생산자 및 소비자가 있다고 가정합니다. 우리는 제작자가 서로 간섭하는 것을 원하지 않지만 소비자는 제작자를 기다릴 필요없이 메시지를 검색 할 수 있어야합니다. 그래서 우리는 단지 객체를 생성합니다
Object writeLock = new Object();
그리고 지금부터 프로듀서는 새 메시지를 추가하려고 할 때마다 다음과 같이 잠급니다.
synchronized(writeLock){
// do something
}
따라서 소비자는 여전히 읽을 수 있으며 생산자는 잠겨 있습니다.
동기화 된 방법
동기화 된 방법에는 두 가지 효과가 있습니다.
첫째, 하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).
둘째, 동기화 된 메소드가 종료되면 동일한 오브젝트에 대한 동기화 된 메소드의 후속 호출과 발생 전 관계를 자동으로 설정합니다. 이를 통해 객체 상태에 대한 변경 사항이 모든 스레드에 표시됩니다.
생성자를 동기화 할 수는 없습니다. 생성자와 동기화 된 키워드를 사용하면 구문 오류가 발생합니다. 생성자를 동기화하는 것은 의미가 없습니다. 객체를 생성하는 스레드 만 생성되는 동안 액세스 할 수 있어야하기 때문입니다.
동기화 된 진술
동기화 된 메소드와 달리, 동기화 된 명령문은 고유 잠금을 제공하는 오브젝트를 지정해야합니다. 대부분은 목록 또는 맵에 대한 액세스를 동기화하기 위해이를 사용하지만 오브젝트의 모든 메소드에 대한 액세스를 차단하고 싶지는 않습니다.
Q : 내장 잠금 및 동기화 동기화는 내장 잠금 또는 모니터 잠금이라고하는 내부 엔터티를 중심으로 구축됩니다. (API 사양은 종종이 엔티티를 단순히 "모니터"라고합니다.) 내장 잠금은 동기화의 두 측면에서 중요한 역할을합니다. 즉 객체 상태에 대한 독점 액세스를 강화하고 가시성에 필수적인 관계를 설정합니다.
모든 객체에는 관련된 고유 잠금이 있습니다. 일반적으로 객체 필드에 독점적이고 일관된 액세스가 필요한 스레드는 객체에 액세스하기 전에 객체의 본질적 잠금을 획득 한 다음 완료되면 본질적 잠금을 해제해야합니다. 스레드는 잠금을 획득하고 잠금을 해제 한 시간 사이에 고유 잠금을 소유한다고합니다. 스레드가 내장 잠금을 소유하는 한 다른 스레드는 동일한 잠금을 얻을 수 없습니다. 다른 스레드는 잠금을 획득하려고 시도 할 때 차단됩니다.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
동기화 된 방법, 블록 및 동기화없이 서로 다른 출력을 교차 점검합니다.
Java 컴파일러가 소스 코드를 바이트 코드로 변환하면 동기화 된 메소드와 동기화 된 블록을 매우 다르게 처리합니다.
JVM이 동기화 된 메소드를 실행할 때 실행중인 스레드는 메소드의 method_info 구조에 ACC_SYNCHRONIZED 플래그가 설정되어 있음을 식별 한 후 자동으로 오브젝트의 잠금을 획득하고 메소드를 호출 한 후 잠금을 해제합니다. 예외가 발생하면 스레드가 자동으로 잠금을 해제합니다.
반면에 메소드 블록 동기화는 오브젝트의 잠금 및 예외 처리를 확보하기위한 JVM의 내장 지원을 생략하며 기능을 명시 적으로 바이트 코드로 작성해야합니다. 동기화 된 블록이있는 메소드의 바이트 코드를 읽으면이 기능을 관리하기위한 12 가지 이상의 추가 작업이 표시됩니다.
다음은 동기화 된 메소드와 동기화 된 블록을 생성하기위한 호출을 보여줍니다.
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
이 synchronizedMethodGet()
메소드는 다음 바이트 코드를 생성합니다.
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
그리고 synchronizedBlockGet()
메소드 의 바이트 코드는 다음과 같습니다.
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
동기화 된 방법과 블록의 한 가지 중요한 차이점은 동기화 된 블록은 일반적으로 잠금 범위를 줄입니다. 잠금 범위는 성능에 반비례하기 때문에 항상 중요한 코드 섹션 만 잠그는 것이 좋습니다. 동기화 된 블록을 사용하는 가장 좋은 예 중 하나는 전체를 잠그는 대신 싱글 톤 패턴의 이중 검사 잠금입니다.getInstance()
방법 Singleton 인스턴스를 만드는 데 사용되는 중요한 코드 섹션 만 잠급니다. 잠금은 한두 번만 필요하므로 성능이 크게 향상됩니다.
동기화 된 메소드를 사용하는 동안 정적 동기화 메소드와 비 정적 동기화 메소드를 모두 혼합하는 경우 특별한주의가 필요합니다.
monitorenter
및 monitorexit
코드를 실행하기 전에.
가장 자주 나는 이것을 사용하여 목록이나 맵에 대한 액세스를 동기화하지만 객체의 모든 메소드에 대한 액세스를 차단하고 싶지 않습니다.
다음 코드에서 목록을 수정하는 하나의 스레드는 맵을 수정하는 스레드를 기다리는 것을 차단하지 않습니다. 메소드가 오브젝트에서 동기화 된 경우 각 메소드는 수정 사항이 충돌하지 않더라도 대기해야합니다.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
동기화 된 블록 사용시 중요 사항 : 잠금 객체로 사용하는 것을주의하십시오!
위의 user2277816의 코드 스 니펫은 문자열 리터럴에 대한 참조가 잠금 오브젝트로 사용된다는 점에서이 점을 보여줍니다. 문자열 리터럴은 Java에서 자동으로 삽입되며 문제를보기 시작해야합니다. 리터럴 "lock"에서 동기화되는 모든 코드는 동일한 잠금을 공유합니다! 이것은 완전히 관련이없는 코드 조각으로 교착 상태를 쉽게 일으킬 수 있습니다.
주의해야 할 것은 단지 String 객체가 아닙니다. 오토 박싱과 valueOf 메소드는 값에 따라 동일한 객체를 재사용 할 수 있기 때문에 박스형 프리미티브도 위험합니다.
자세한 내용은 https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused를 참조하십시오.
메소드 레벨에서 잠금을 사용하는 것은 너무 무례합니다. 전체 메소드를 잠금으로써 공유 자원에 액세스하지 않는 코드를 잠그는 이유는 무엇입니까? 각 객체에는 잠금이 있으므로 더미 객체를 만들어 블록 수준 동기화를 구현할 수 있습니다. 전체 레벨을 잠그지 않기 때문에 블록 레벨이 더 효율적입니다.
여기 몇 가지 예
방법 수준
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
블록 레벨
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[편집하다]
의 경우 Collection
처럼 Vector
그리고 Hashtable
그들은 동기화하면됩니다 ArrayList
또는 HashMap
하지 당신이 키워드를 동기화 또는 컬렉션 동기화 방법 호출 세트 필요 :
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
유일한 차이점 : 동기화 된 블록은 동기화 된 방법과 달리 세분화 된 잠금을 허용합니다
원래 synchronized
블록이나 메소드는 메모리 불일치 오류를 피함으로써 스레드 안전 코드를 작성하는 데 사용되었습니다.
이 질문은 매우 오래되었으며 지난 7 년 동안 많은 것들이 바뀌 었습니다. 스레드 안전을 위해 새로운 프로그래밍 구성이 도입되었습니다.
synchronied
블록 대신 고급 동시성 API를 사용하여 스레드 안전성을 확보 할 수 있습니다 . 이 문서 페이지 는 스레드 안전성을 달성하기위한 좋은 프로그래밍 구성을 제공합니다.
잠금 개체 는 많은 동시 응용 프로그램을 단순화하는 잠금 관용구를 지원합니다.
실행 자는 스레드를 시작하고 관리하기위한 고급 API를 정의합니다. java.util.concurrent가 제공하는 실행기 구현은 대규모 애플리케이션에 적합한 스레드 풀 관리를 제공합니다.
동시 수집을 사용하면 대규모 데이터 수집을보다 쉽게 관리 할 수 있으며 동기화 필요성을 크게 줄일 수 있습니다.
원자 변수 에는 동기화를 최소화하고 메모리 일관성 오류를 방지하는 기능이 있습니다.
ThreadLocalRandom (JDK 7)은 여러 스레드에서 의사 난수를 효율적으로 생성합니다.
동기화를위한 더 나은 대체 는 API 를 사용하는 ReentrantLock입니다Lock
동기화 된 메소드 및 명령문을 사용하여 액세스 된 암시 적 모니터 잠금과 동일한 기본 동작 및 의미를 갖는 재진입 상호 배제 잠금.
잠금이있는 예 :
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
java.util.concurrent 및 java.util.concurrent.atomic을 참조하십시오.다른 프로그래밍 구성에 대해서는 패키지도 .
이 관련 질문도 참조하십시오.
이미 언급했듯이 동기화 된 기능이 "this"만 사용하는 경우 동기화 된 블록은 사용자 정의 변수를 잠금 객체로 사용할 수 있습니다. 물론 동기화해야 할 기능 영역을 조작 할 수 있습니다. 그러나 모든 사람들은 "this"를 잠금 객체로 사용하여 전체 기능을 다루는 동기화 된 기능과 블록의 차이점은 없다고 말합니다. 그것은 사실이 아니며, 두 상황에서 생성되는 바이트 코드의 차이입니다. 동기화 된 블록 사용의 경우 "this"에 대한 참조를 보유하는 로컬 변수를 할당해야합니다. 결과적으로 함수의 크기가 조금 더 커질 것입니다 (함수 수가 적은 경우에는 관련이 없음).
차이점에 대한 자세한 설명은 http://www.artima.com/insidejvm/ed2/threadsynchP.html을 참조 하십시오.
동기화 된 메소드의 경우 오브젝트에서 잠금이 획득됩니다. 그러나 동기화 블록을 사용하면 잠금을 획득 할 객체를 지정할 수 있습니다.
예 :
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
나는 이것이 오래된 질문이라는 것을 알고 있지만 여기에 대한 답변을 빨리 읽었을 때 누군가가 때때로 그것을 언급하는 것을 보지 못했습니다. synchronized
메서드가 잘못된 잠금 장치 .
실제로 Java 동시성 (72 페이지)에서 :
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
위의 코드는 모양 스레드 안전되는합니다. 그러나 실제로는 그렇지 않습니다. 이 경우 클래스의 인스턴스에서 잠금을 얻습니다. 그러나 해당 메소드를 사용하지 않는 다른 스레드 가 목록 을 수정할 수 있습니다. 올바른 접근 방식은
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
위의 코드는 동기화 된 블록이 완료 될 때까지 목록 을 수정 하려는 모든 스레드가 목록 을 수정 하지 못하도록 차단 합니다 .
List
반드시 동기화 할 필요가없는 코드의 로그가있는 경우 성능 문제가 발생할 수 있습니다
실제적으로, 동기화 된 블록에 비해 동기화 된 방법의 장점은 더 바보 저항 적이라는 것입니다. 잠그는 임의의 객체를 선택할 수 없으므로 동기화 된 메소드 구문을 잘못 사용하여 문자열 리터럴 잠금 또는 스레드 아래에서 변경되는 변경 가능한 필드의 내용 잠금과 같은 어리석은 일을 할 수 없습니다.
반면에 동기화 된 메소드를 사용하면 객체에 대한 참조를 얻을 수있는 스레드가 잠금을 획득하지 못하게 할 수 없습니다.
따라서 동기화 된 메소드를 메소드에서 수정 자로 사용하는 것이 암소를 아프게하지 않도록 보호하는 데 도움이되는 반면, 개인 최종 잠금 오브젝트와 함께 동기화 된 블록을 사용하면 암소로부터 자신의 코드를 보호하는 것이 더 좋습니다.
Java 스펙 요약에서 : http://www.cs.cornell.edu/andru/javaspec/17.doc.html
동기화 된 명령문 (§14.17)은 객체에 대한 참조를 계산합니다. 그런 다음 해당 오브젝트에 대한 잠금 조치를 시도하고 잠금 조치가 완료 될 때까지 더 이상 진행하지 않습니다. ...
동기화 된 메소드 (§8.4.3.5)는 호출 될 때 자동으로 잠금 조치를 수행합니다. 잠금 조치가 완료 될 때까지 본문이 실행되지 않습니다. 메소드가 인스턴스 메소드 인 경우, 메소드는 호출 된 인스턴스 (즉, 메소드 본문 실행 중이 오브젝트로 알려진 오브젝트)와 연관된 잠금을 잠급니다. 메소드가 static 인 경우 메소드가 정의 된 클래스를 나타내는 Class 오브젝트와 연관된 잠금을 잠급니다. ...
이 설명을 바탕으로, 나는 대부분의 이전 답변이 정확하다고 말하고 동기화 된 메소드는 정적 메소드에 특히 유용 할 수 있습니다. 한정된."
편집 : 원래 이들은 실제 Java 사양의 인용이라고 생각했습니다. 이 페이지는 사양에 대한 요약 / 설명 일뿐입니다.
TLDR; synchronized
수정 자나 synchronized(this){...}
표현식을 사용 하지 말고 개인 오브젝트를 보유하는 최종 인스턴스 필드는 synchronized(myLock){...}
어디에 있습니까 myLock
?
synchronized
메소드 선언 에서 수정자를 사용하는 synchronized(..){ }
것과 메소드 본문 의 표현식 의 차이점 은 다음과 같습니다.
synchronized
방법의 서명에 지정된 수정
synchronized(this) { .... }
하며this
비 정적 메소드에서 선언 될 때 오브젝트를 잠금으로 사용 하거나 정적 메소드에서 선언 될 때 엔 클로징 클래스를 사용합니다.synchronized(...){...}
표현을 할 수 있습니다
그러나 synchronized
수정자를 사용하거나 잠금 객체로 사용하는 경우 ( synchronized(...) {...}
와 this
같이 synchronized(this) {...}
) 동일한 단점이 있습니다. 둘 다 동기화 할 잠금 개체로 자체 인스턴스를 사용합니다. 이것은 위험하므로 객체 자체뿐만 아니라 임의의 잠재적으로 심각한 부작용 (성능 저하와 함께 동기 로크로 사용할 수있는 객체에 대한 참조를 보유하고 다른 외부 물체 / 코드 교착 ).
따라서 가장 좋은 방법은 synchronized
수정 자나 synchronized(...)
표현식 this
을 잠금 객체 와 함께 사용하지 않고이 객체의 전용 잠금 객체를 사용하는 것입니다. 예를 들면 다음과 같습니다.
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
여러 잠금 개체를 사용할 수도 있지만 중첩 사용시 교착 상태가 발생하지 않도록 특별한주의를 기울여야합니다.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
이 질문이 Thread Safe Singleton 과 Double check locking을 사용한 Lazy 초기화 의 차이점에 관한 것 입니다. 특정 싱글 톤을 구현해야 할 때 항상이 기사를 참조하십시오.
글쎄, 이것은 Thread Safe Singleton입니다 .
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
장점 :
게으른 초기화가 가능합니다.
스레드 안전합니다.
단점 :
- getInstance () 메소드는 동기화되어 여러 스레드가 동시에 액세스 할 수 없으므로 성능이 저하됩니다.
이것은 이중 점검 잠금을 사용한 지연 초기화입니다 .
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
장점 :
게으른 초기화가 가능합니다.
또한 스레드 안전합니다.
키워드 동기화로 인해 성능이 저하되었습니다.
단점 :
처음에는 성능에 영향을 줄 수 있습니다.
단점으로. 이중 체크 잠금 방식은 고성능 멀티 스레드 애플리케이션에 사용될 수 있습니다.
자세한 내용은이 기사를 참조하십시오.
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
스레드와 동기화 1) 작동하지 않는 스레드에서 동기화 된 것을 사용하지 마십시오. (this)와 동기화하면 현재 스레드가 잠금 스레드 오브젝트로 사용됩니다. 각 스레드는 다른 스레드와 독립적이므로 동기화 조정이 없습니다. 2) 코드 테스트에 따르면 Mac의 Java 1.6에서는 메소드 동기화가 작동하지 않습니다. 3) synchronized (lockObj) 여기서 lockObj는 동기화하는 모든 스레드의 공통 공유 객체입니다. 4) ReenterantLock.lock () 및 .unlock ()이 작동합니다. 이에 대한 Java 학습서를 참조하십시오.
다음 코드는 이러한 점을 보여줍니다. 또한 ArrayList로 대체되는 스레드 안전 Vector가 포함되어있어 Vector에 추가하는 많은 스레드가 정보를 잃지 않고 ArrayList와 동일한 정보가 손실 될 수 있음을 보여줍니다. 0) 현재 코드는 경쟁 조건으로 인한 정보 손실을 표시합니다. A) 현재 레이블이있는 A 행을 주석 처리하고 그 위의 A 행을 주석 해제 한 다음 실행하면 메소드가 데이터를 유실하지만 그렇지 않아야합니다. B) A 단계를 취소하고 주석 처리를 제거하고 // 블록 종료}. 그런 다음 데이터 손실이없는 결과를 보려면 실행하십시오. C) 주석을 해제하십시오. C의 주석 처리를 제거하십시오. 모든 변형을 완료 할 시간이 없습니다. 이것이 도움이되기를 바랍니다. (this)에서 동기화하거나 메소드 동기화가 작동하는 경우 테스트 한 Java 및 OS 버전을 지정하십시오. 감사합니다.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class