0. 개척 질문 코드
다음 코드는 두 개의 스레드가 동시에 i를 축적하는 카운터를 보여줍니다. 우리가 기대하는 결과는 확실히 i = 200000입니다. 그러나 여러 번 실행 한 후에는 I의 값이 항상 200000보다 작을 것임을 알게 될 것입니다. 이것은 두 스레드가 동시에 i를 쓸 때 스레드 중 하나의 결과가 다른 스레드를 덮어 쓰기 때문입니다.
공개 클래스 회계 동기화는 실행 가능 {static int i = 0; 공개 무효 인상 () {i ++; } @override public void run () {for (int j = 0; }} public static void main (String [] args)은 InterruptedException {AccountingSync AccountingSync = new AccountingSync (); 스레드 T1 = 새 스레드 (AccountingSync); 스레드 t2 = 새 스레드 (AccountingSync); t1.start (); t2.start (); t1.join (); t2.join (); System.out.println (i); }} 이 문제를 근본적으로 해결하려면 작동 할 때 여러 스레드를 완전히 동기화해야합니다. 즉, Thread A가 I를 쓸 때 Thread B는 글을 쓸 수 없을뿐만 아니라 읽을 수 없습니다.
1. 동기화 된 키워드의 역할
동기화 된 키워드의 기능은 실제로 스레드 간의 동기화를 실현하는 것입니다. 그 작업은 동기화 된 코드를 잠그므로 한 번만 한 번만 동기화 블록을 입력하여 스레드 간의 보안을 보장하는 것입니다. 위 코드와 마찬가지로 I ++ 작업은 동시에 다른 스레드에서만 실행할 수 있습니다.
2. 동기화 된 키워드의 사용
개체 잠금 지정 : 주어진 객체를 잠그고 동기화 코드 블록을 입력하여 주어진 객체의 잠금을 얻습니다.
인스턴스 방법에 직접 작용 : 현재 인스턴스를 잠그는 것과 같습니다. 동기 코드 블록을 입력하면 현재 인스턴스의 잠금을 얻어야합니다 (스레드를 만들 때 동일한 실행 가능한 인스턴스를 사용해야합니다).
정적 방법에 직접 작용 : 현재 클래스를 잠그는 것과 같습니다. 동기 코드 블록을 입력하기 전에 현재 클래스의 잠금을 얻어야합니다.
2.1 잠금 할 객체를 지정하십시오
다음 코드는 주어진 객체와 동기화됩니다. 여기에 주어진 객체가 정적이어야한다는 메모가 있습니다. 그렇지 않으면 우리는 새로운 스레드를 새로울 때마다 서로 객체를 공유하지 않으므로 잠금의 의미가 더 이상 존재하지 않을 것입니다.
공개 클래스 회계 동기화는 실행 가능 {최종 정적 개체 객체 = new Object (); 정적 int i = 0; 공개 무효 인상 () {i ++; } @Override public void run () {for (int j = 0; }}} public static void main (String [] args)은 InterruptedException {Thread T1 = New Thread (new AccountingSync ()); 스레드 t2 = 새 스레드 (new AccountingSync ()); t1.start (); t2.start (); t1.join (); t2.join (); System.out.println (i); }} 2.2 인스턴스 방법에 직접 작동합니다
동기화 된 키워드는 인스턴스 메소드에서, 즉 증가 () 메소드를 입력하기 전에 스레드가 현재 인스턴스의 잠금을 얻어야합니다. 이를 위해서는 스레드 인스턴스를 만들 때 동일한 런닝 가능한 객체 인스턴스를 사용해야합니다. 그렇지 않으면 스레드의 잠금이 동일한 인스턴스에 있지 않으므로 잠금/동기화 문제에 대해 이야기 할 방법이 없습니다.
공개 클래스 회계 동기화는 실행 가능 {static int i = 0; public synchronized void alload () {i ++; } @override public void run () {for (int j = 0; }} public static void main (String [] args)은 InterruptedException {AccountingSync AccountingSync = new AccountingSync (); 스레드 T1 = 새 스레드 (AccountingSync); 스레드 t2 = 새 스레드 (AccountingSync); t1.start (); t2.start (); t1.join (); t2.join (); System.out.println (i); }} 인스턴스 메소드에서 키워드의 올바른 사용법을 설명하려면 기본 방법의 첫 세 줄에주의를 기울이십시오.
2.3 정적 방법에 직접 작용합니다
동기화 된 키워드를 정적 메소드에 적용하려면 위의 예에서와 동일한 실행 가능한 메소드를 가리키기 위해 두 스레드를 사용 할 필요가 없습니다. 메소드 블록은 현재 인스턴스가 아닌 현재 클래스의 잠금을 요청해야하므로 스레드는 여전히 올바르게 동기화 될 수 있습니다.
공개 클래스 회계 동기화는 실행 가능 {static int i = 0; public static synchronized void alload () {i ++; } @override public void run () {for (int j = 0; }} public static void main (String [] args)은 InterruptedException {Thread T1 = New Thread (new AccountingSync ()); 스레드 t2 = 새 스레드 (new AccountingSync ()); t1.start (); t2.start (); t1.join (); t2.join (); System.out.println (i); }}3. 잘못된 잠금
위의 예에서, 카운터 응용 프로그램이 필요한 경우 데이터의 정확성을 보장하기 위해서는 자연스럽게 카운터를 잠그야하므로 다음 코드를 작성할 수 있음을 알고 있습니다.
공개 클래스 Badlockoninteger는 런닝 가능한 {정적 정수 i = 0; @override public void run () {for (int j = 0; j <10000000; j ++) {synchronized (i) {i ++; }}} public static void main (string [] args)은 중단 exception {badlockoninteger badlockoninteger = new BadlockonInteger (); 스레드 t1 = 새 스레드 (badlockoninteger); 스레드 t2 = 새로운 스레드 (badlockoninteger); t1.start (); t2.start (); t1.join (); t2.join (); System.out.println (i); }}위의 코드를 실행하면 출력 I가 매우 작다는 것을 알게됩니다. 이것은 스레드가 안전하지 않음을 의미합니다.
이 문제를 설명하려면 정수로 시작해야합니다. Java에서 정수는 변하지 않는 대상입니다. 문자열처럼 객체가 생성되면 수정할 수 없습니다. 정수 = 1이 있으면 항상 1이됩니다.이 개체 = 2를 원한다면 어떻게해야합니까? 정수 만 재현 할 수 있습니다. 각각의 I ++ 후에는 정수의 가치를 호출하는 것과 같습니다. 정수의 가치의 소스 코드를 살펴 보겠습니다.
공개 정적 정수 값 (int i) {if (i> = integercache.low && i <= integercache.high) return integercache.cache [i + (-integercache.low)]; 새로운 정수를 반환합니다 (i);} integer.valueof ()는 실제로 새로운 정수 객체를 반환하고 값을 i에 복사하는 경향이있는 공장 방법입니다.
따라서 우리는 문제의 이유를 알고 있습니다. 여러 스레드 사이에서 i ++가 나중에 나오기 때문에 새 객체를 가리키면 스레드가 잠글 때마다 다른 객체 인스턴스를로드 할 수 있습니다. 솔루션은 매우 간단합니다. 위의 세 가지 동기화 방법 중 하나를 사용하여 해결할 수 있습니다.