다음과 같이 코드 코드를 복사합니다.
공개 동기화 무효 실행()
{
}
위 코드에서 볼 수 있듯이 void와 public 사이에 동기화 키워드만 추가하면 run 메소드를 동기화할 수 있습니다. 즉, 동일한 Java 클래스의 객체 인스턴스에 대해서는 run 메소드만 동기화할 수 있습니다. 동시에 한 스레드에서 호출되며 현재 실행이 실행된 후에만 다른 스레드에서 호출할 수 있습니다. 현재 스레드가 run 메서드에서 Yield 메서드를 실행하더라도 잠시 동안만 일시 중지됩니다. 다른 스레드는 run 메소드를 실행할 수 없으므로 현재 스레드는 결국 실행을 계속하게 됩니다. 먼저 다음 코드를 살펴보세요.
동기화된 키워드는 하나의 객체 인스턴스에만 바인딩됩니다.
다음과 같이 코드 코드를 복사합니다.
수업 테스트
{
공개 동기화 무효 메소드()
{
}
}
공개 클래스 Sync는 Runnable을 구현합니다.
{
개인 테스트 테스트;
공개 무효 실행()
{
test.method();
}
공개 동기화(테스트 테스트)
{
this.test = 테스트;
}
public static void main(String[] args)에서 예외가 발생했습니다.
{
테스트 test1 = new Test();
테스트 test2 = new Test();
동기화 sync1 = 새 동기화(test1);
동기화 sync2 = 새로운 동기화(test2);
새로운 스레드(sync1).start();
새로운 스레드(sync2).start();
}
}
Test 클래스의 메서드 메서드는 동기식입니다. 그러나 위의 코드는 Test 클래스의 두 인스턴스를 생성하므로 test1과 test2의 메서드 메서드가 별도로 실행됩니다. 메서드를 동기화하려면 다음 코드에 표시된 대로 Sync 클래스의 인스턴스를 만들 때 동일한 Test 클래스의 인스턴스를 생성자에 전달해야 합니다.
동기화 sync1 = 새 동기화(test1);
동기화를 사용하여 비정적 메서드를 동기화할 수 있을 뿐만 아니라 동기화를 사용하여 정적 메서드를 동기화할 수도 있습니다. 예를 들어 메소드 메소드는 다음과 같이 정의할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
수업 테스트
{
공개 정적 동기화 무효 메소드() { }
}
다음과 같이 Test 클래스의 객체 인스턴스를 만듭니다.
테스트 테스트 = new Test();
정적 메서드의 경우 동기화 키워드를 추가하면 메서드가 동기화됩니다. test.method()를 사용하든 Test.method()를 사용하여 메서드 메서드를 호출하든 메서드가 동기화되며 여러 개가 존재하지 않습니다. 비정적 메소드의 인스턴스.
23가지 디자인 패턴 중 싱글톤 모드는 전통적인 방법에 따라 디자인된 경우에도 스레드에 안전하지 않습니다. 다음 코드는 스레드에 안전하지 않은 싱글톤 모드입니다.
다음과 같이 코드 코드를 복사합니다.
패키지 테스트;
// 스레드로부터 안전한 싱글톤 모드
클래스 싱글턴
{
비공개 정적 싱글톤 샘플;
개인 싱글턴()
{
}
공개 정적 싱글턴 getInstance()
{
if (샘플 == null)
{
Thread.yield(); // 싱글턴 모드의 스레드 불안정성을 증폭시키기 위해
샘플 = 새로운 싱글톤();
}
샘플 반환;
}
}
공개 클래스 MyThread는 Thread를 확장합니다.
{
공개 무효 실행()
{
싱글톤 싱글톤 = Singleton.getInstance();
System.out.println(singleton.hashCode());
}
공개 정적 무효 메인(문자열[] 인수)
{
스레드 스레드[] = 새 스레드[5];
for (int i = 0; i < thread.length; i++)
스레드[i] = new MyThread();
for (int i = 0; i < thread.length; i++)
스레드[i].start();
}
}
싱글톤 모드의 스레드 안전하지 않음을 표시하기 위해 위 코드에서 Yield 메서드가 호출됩니다. 이 줄을 제거해도 위 구현은 여전히 스레드 안전하지 않지만 발생 가능성은 훨씬 적습니다.
프로그램을 실행한 결과는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
25358555
26399554
7051261
29855319
5383406
위의 실행 결과는 실행 환경에 따라 다를 수 있지만 일반적으로 5줄의 출력이 완전히 동일하지는 않습니다. 이 출력에서 볼 수 있듯이 getInstance 메소드를 통해 얻은 객체 인스턴스가 5개가 있지만 예상한 것과는 다릅니다. 이는 스레드가 Thread.yield()를 실행할 때 CPU 리소스를 다른 스레드에 넘겨주기 때문입니다. 스레드 간 전환 시 Singleton 객체 인스턴스를 생성하는 문이 실행되지 않으므로 이들 스레드는 모두 if 판단을 통과하므로 통과하는 스레드 수에 따라 5개의 객체 인스턴스가 생성되거나 4개가 생성될 수도 있습니다. 싱글톤 객체를 생성하기 전의 if 판단으로, 실행될 때마다 결과가 달라질 수 있습니다.)
위의 싱글톤 모드를 스레드로부터 안전하게 만들려면 동기화된 키워드를 getInstance에 추가하기만 하면 됩니다. 코드는 다음과 같습니다:
공개 정적 동기화 싱글톤 getInstance() { }
물론, Singleton 변수를 정의할 때 Singleton 객체를 생성하는 더 간단한 방법이 있습니다. 코드는 다음과 같습니다.
private static final Singleton 샘플 = new Singleton();
그런 다음 getInstance 메서드에서 직접 샘플을 반환하면 됩니다. 이 방법은 간단하지만 getInstance 메서드에서 Singleton 객체를 생성하는 데에는 유연성이 없습니다. 독자는 특정 요구 사항에 따라 다양한 방법을 사용하여 싱글톤 패턴을 구현하도록 선택할 수 있습니다.
동기화된 키워드를 사용할 때 주의해야 할 네 가지 사항은 다음과 같습니다.
1. 동기화된 키워드는 상속될 수 없습니다.
동기화를 사용하여 메소드를 정의할 수 있지만 동기화는 메소드 정의의 일부가 아니므로 동기화 키워드를 상속할 수 없습니다. 상위 클래스의 메서드가 동기화 키워드를 사용하고 해당 메서드가 하위 클래스에서 재정의된 경우 하위 클래스의 메서드는 기본적으로 동기화되지 않으며 하위 클래스에서 명시적으로 지정해야 합니다. 물론, 하위 클래스의 메소드에서 해당 메소드를 호출할 수도 있습니다. 이렇게 하면 하위 클래스의 메소드가 동기적이지 않더라도 하위 클래스는 상위 클래스의 메소드를 호출하게 됩니다. 하위 클래스는 동기화와 동일합니다. 이 두 가지 방법에 대한 예제 코드는 다음과 같습니다.
하위 클래스 메서드에 동기화 키워드 추가
다음과 같이 코드 코드를 복사합니다.
클래스 부모
{
공개 동기화 무효 메소드() { }
}
클래스 Child가 Parent를 확장합니다.
{
공개 동기화 무효 메소드() { }
}
하위 클래스 메서드에서 상위 클래스의 동기화 메서드를 호출합니다.
다음과 같이 코드 코드를 복사합니다.
클래스 부모
{
공개 동기화 무효 메소드() { }
}
클래스 Child가 Parent를 확장합니다.
{
공개 무효 메소드() { super.method() }
}
2. 인터페이스 메소드 정의 시에는 syncified 키워드를 사용할 수 없습니다.
3. 생성자는 동기화 키워드를 사용할 수 없지만 동기화를 위해 다음 섹션에서 설명할 동기화 블록을 사용할 수 있습니다.
4. 동기화는 자유롭게 배치할 수 있습니다.
이전 예제에서는 동기화 키워드가 메서드의 반환 유형 앞에 배치되었습니다. 그러나 동기화가 배치될 수 있는 유일한 장소는 아닙니다. 비정적 메서드에서는 동기화를 메서드 정의 앞에 배치할 수도 있습니다. 정적 메서드에서는 동기화를 정적 메서드 앞에 배치할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
공개 동기화 무효 메소드();
동기화된 공개 무효 메소드();
공개 정적 동기화 무효 메소드();
공개 동기화 정적 무효 메소드();
동기화된 공개 정적 무효 메소드();
그러나 메소드 반환 유형 뒤에는 동기화를 배치할 수 없습니다. 예를 들어 다음 코드는 잘못되었습니다.
다음과 같이 코드 코드를 복사합니다.
공개 무효 동기화 방법();
공개 정적 무효 동기화 방법();
동기화 키워드는 클래스 변수가 아닌 메서드를 동기화하는 데만 사용할 수 있습니다. 다음 코드도 잘못되었습니다.
다음과 같이 코드 코드를 복사합니다.
공개 동기화 int n = 0;
공개 정적 동기화 int n = 0;
동기화된 키워드 동기화 방법을 사용하는 것이 가장 안전한 동기화 방법이지만, 동기화된 키워드를 과도하게 사용하면 불필요한 리소스 소비 및 성능 손실이 발생합니다. 표면적으로는 동기화가 메소드를 잠그는 것처럼 보이지만 실제로는 동기화가 클래스를 잠급니다. 즉, 비정적 메서드인 method1과 method2를 모두 정의할 때 동기화를 사용하면 method1이 실행되기 전에는 method2를 실행할 수 없습니다. 정적 메서드와 비정적 메서드의 상황은 비슷합니다. 그러나 정적 메서드와 비정적 메서드는 서로 영향을 미치지 않습니다. 다음 코드를 살펴보세요.
다음과 같이 코드 코드를 복사합니다.
패키지 테스트;
공개 클래스 MyThread1은 Thread를 확장합니다.
{
공개 문자열 메소드명;
공개 정적 무효 메소드(문자열 s)
{
System.out.println(s);
동안(사실)
}
공개 동기화 무효 메소드1()
{
method("비정적 method1 메서드");
}
공개 동기화 무효 메소드2()
{
method("비정적 method2 메서드");
}
공개 정적 동기화 무효 메소드3()
{
method("정적 메서드3 메서드");
}
공개 정적 동기화 무효 메소드4()
{
method("정적 method4 메서드");
}
공개 무효 실행()
{
노력하다
{
getClass().getMethod(methodName).invoke(this);
}
잡기 (예외 e)
{
}
}
public static void main(String[] args)에서 예외가 발생했습니다.
{
MyThread1 myThread1 = 새로운 MyThread1();
for (int i = 1; i <= 4; i++)
{
myThread1.methodName = "메서드" + String.valueOf(i);
새로운 스레드(myThread1).start();
수면(100);
}
}
}
실행 결과는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
비정적 method1 방법
정적 방법3 방법
위의 실행 결과를 보면 method1과 method3이 완료되기 전에는 method2와 method4를 실행할 수 없다는 것을 알 수 있습니다. 따라서 동기화 키워드를 사용하여 클래스에서 비정적 메서드를 정의하면 이 클래스에서 동기화 키워드를 사용하여 정의된 모든 비정적 메서드에 영향을 미친다는 결론을 내릴 수 있습니다. 정적 메서드가 정의되면 클래스에서 동기화된 키워드를 사용하여 정의된 모든 정적 메서드에 영향을 미칩니다. 이는 데이터 테이블의 테이블 잠금과 비슷합니다. 레코드가 수정되면 시스템이 전체 테이블을 잠급니다. 따라서 이 동기화 방법을 광범위하게 사용하면 프로그램 성능이 크게 저하됩니다.