>  기사  >  Java  >  Java를 처음 배우는 초보자를 위한 메모 (7)

Java를 처음 배우는 초보자를 위한 메모 (7)

黄舟
黄舟원래의
2016-12-20 13:52:481345검색


갑자기 동기화 키워드 사용법 등 아직 이해해야 할 사항이 많다는 것을 깨달았습니다. 어제 연결 풀 소켓 생성에 대한 연구를 하다가 그렇지 않은 경우를 발견했기 때문입니다. 이 개념이 이해가 안가서 전혀 진행이 불가능해서 소켓에 대한 애정을 식히고 싱크로나이즈드를 다시 돌아보기로 했습니다.

아침 내내 Think in java를 읽고나니 아직도 많이 부족하다는 생각이 듭니다. 효과적이고 즉시 작성해야 합니다. 내 뇌의 재사용성이 매우 낮다는 느낌이 듭니다. 항상 새로운 메모리 개체를 생성해야 하므로 유사한 작업이 더 많이 필요합니다.

동기화의 사용법을 이해하려면 먼저 동기화가 어떤 문제를 해결하는 데 사용되는지 알아야 합니다. 다음은 질문이 발생할 수 있는 비동기화의 예입니다.

이 예에서는 두 개의 스레드 클래스를 생성합니다. 하나는 TwoCounter라고 하며 해당 작업은 두 개를 누적하는 것입니다. 1부터 시작하면 바로 동기화를 위해 사용한다고 생각하실 겁니다. 또 다른 개체는 이름에서 알 수 있듯이 작업을 모니터링하는 데 사용됩니다. ​​​​TwoCounter 스레드에 있는 두 카운터의 값이 동일하다는 것은 의미 없는 작업인 것 같습니다. 왜 두 카운터의 값이 같지 않을 수 있습니까??

하지만 그렇지 않습니다. 먼저 프로그램을 살펴보겠습니다. 이 프로그램을 보기 전에 Java 14.2.1의 Think를 살펴보면 실제로 해당 섹션에 제공된 예제를 기반으로 내 프로그램이 단순화되었습니다. . 메인 클래스는 Sharing2

class TwoCounter extends Thread {
PRIvate int count1 = 0, count2 = 0;
private boolean start=false;
public void start(){로 변경됩니다.
if (!started) file://스레드에서 Start 메서드에 대한 여러 호출 방지
{
started=true;
super.start();
}
}
public void run() {
while (true) {
count1++;
file ://TwoCounter가 이 지점까지 실행되고 CPU 타임 슬라이스가 Watcher에 할당되면 물론 값은 이때 Watcher가 읽는 두 개의 카운터가 다를 가능성이 있습니다. "이것은 스레드의 특성 때문입니다. 스레드는 언제든지 일시 중지(일시 중지)될 수 있습니다. 따라서 위 두 줄의 실행 시간 사이에 때때로 실행이 일시 중지될 수 있습니다. 동시에 Watcher 스레드는 그리고 이때 비교가 수행되어 카운터가 동일하지 않게 됩니다." (Java로 생각)
count2++;
System.out.println("Count1="+count1+",Count2= "+count2) ;
try {
sleep(500);
} catch(InterruptedException e){}
}
}

public void synchTest() {
Sharing2.incrementaccess();
if(count1 != count2)
System.out.println("Unsynched");//동기화되지 않은 항목이 발견되면 즉시 표시됩니다
}
}

class Watcher 확장 스레드 {
private Sharing2 p;
public Watcher(Sharing2 p) {
this.p = p;
start();
}
public void run( ) {
while(true) {
p.s.synchTest();
try {
sleep(500);
} catch(InterruptedException e){}
}
}
}

public class Sharing2 {
TwoCounter s;
private static int accessCount = 0;
public static void incrementAccess() {
accessCount++;
System.out.println("accessCount="+accessCount);
}
public static void main(String[] args) {
Sharing2 aaa = new Sharing2();
aaa.s=new TwoCounter ();
aaa.s.start();//TwoCounter 스레드 열기
new Watcher(aaa);//Watcher 스레드 열기
}
}

위 댓글 비동기화 현상이 발생할 수 있다고 아주 명확하게 설명했는데, 이상한 점은 실행해 보면 비동기화 현상이 한 번도 발생하지 않았다는 것입니다. 프로그램의 count1++ 및 count2++는 거의 동시에 삽입할 수 없습니다. 그런데 Think in Java의 프로그램이 실행 후 확실히 동기화되지 않는 이유는 무엇입니까? 차이점은 내 프로그램이 더 간단하고 GUI를 사용하지 않고 명령줄에서 실행한다는 것입니다. 애플릿 모드에서 실행하거나 Windows 기본 창에서 실행하는 것이 더 비싸서 감시자가 활용할 수 있기 때문입니까? count1++ 과 count2++ 사이에 루프 문을 추가합니다. 인위적으로 간격을 늘리는 목적은 감시자가 삽입되도록 허용하여 모니터링되는 count1이 count2와 같지 않아 비동기화가 발생하는 상황을 초래하는 것입니다.
......
count1++;
for(int i=0;icount2++;
......

OK! 프로그램을 다시 실행하면 곧 보일 것입니다. 비동기화 현상이 발생하여 지금의 분석이 맞는 것으로 보입니다. 그런데 이상한 점은 Unsynchronized가 한 번 출력된 후에는 다시는 나타나지 않는다는 것입니다. , 감시자 스레드가 두 개의 카운터만 감지했습니다. 이것은 약간 우울한 느낌이 듭니다. 우연인지 불가피한지, 대기 시간이 지나면 반드시 비동기화 출력이 발생하게 됩니다. 잊어버리세요. 이 문제는 일단 제쳐두고 계속 진행하겠습니다.
비동기화 문제가 발생했기 때문에 솔루션이 동기화된 것이 분명합니다. TwoCounter의 실행 메소드와 SynchTest 메소드를 동기화 메소드로 변경한다는 것은 무엇을 의미합니까? Java에서 생각하기를 참조하십시오. 14.2.2절에는 아주 상세하고 꼼꼼한 설명이 담겨 있습니다. 특히 우리가 일반적으로 객체 잠금이라고 부르는 모니터의 개념은 책에서 아주 명확하게 설명되어 있습니다. 다음:
class TwoCounter extends Thread {
publicsynchronous void run() {
while(true) {
count1++;
count2++;
System.out.println("Count1= "+count1+",Count2="+count2);
시도 {
sleep(500);
} catch(InterruptedException e){}
}
}

public 동기화 void synchTest() {
Sharing2.incrementAccess();
if(count1 != count2)
System.out.println("Unsynched");//동기화되지 않은 항목이 발견되면 즉시 표시됩니다
}
}

다른 사항을 생략하면 문제 해결이 실제로 매우 간단하다는 의미입니다. 하하.
run()과 synchTest()는 모두 "동기적"이라는 점에 유의하세요. " . 메소드 중 하나만 동기화되면 다른 메소드는 객체 잠금을 무시하고 방해 없이 호출될 수 있습니다. 따라서 중요한 규칙을 기억해야 합니다. 중요한 공유 리소스에 액세스하는 모든 방법은 동기화로 설정되어야 합니다. 그렇지 않으면 제대로 작동하지 않습니다.

이제 새로운 문제에 직면했습니다. Watcher2는 전체 run() 메서드가 "동기"로 설정되어 있기 때문에 무슨 일이 일어나고 있는지 결코 볼 수 없습니다. 그리고 run()은 각 객체에 대해 실행되어야 하므로 잠금은 결코 열릴 수 없으며 synchTest()는 호출되지 않습니다. accessCount가 전혀 변경되지 않았기 때문에 이 결과를 볼 수 있습니다.

이 문제를 해결하기 위해 취할 수 있는 한 가지 방법은 run()에서 코드의 일부만 분리하는 것입니다. 이런 방식으로 분리하려는 코드 부분을 "중요 영역"이라고 하며, 동기화 키워드는 중요 영역을 설정하는 데 다양한 방법으로 사용해야 합니다. Java는 "동기화 블록"을 통해 중요한 영역에 대한 지원을 제공합니다. 이번에는 동기화 키워드를 사용하여 객체 잠금이 그 안에 포함된 코드를 동기화하는 데 사용됨을 나타냅니다. 아래와 같이:

synchronized(syncObject) {
// 이 코드는
// 모든 스레드가 syncObject를 존중한다는 가정 하에 한 번에 하나의 스레드에서만 액세스할 수 있습니다
// 잠금
}

동기화 블록에 들어가기 전에 synchObject에 대한 잠금을 획득해야 합니다. 다른 스레드가 잠금을 획득한 경우 블록은 진입할 수 없으며 잠금이 해제될 때까지 기다려야 합니다.
전체 run()에서 동기화된 키워드를 삭제하고 두 개의 키 라인을 둘러싼 동기화된 블록으로 대체하면 Sharing2 예제에 대한 수정이 완료됩니다. 그러면 어떤 물건을 자물쇠로 사용해야 할까요? 그 객체는 synchTest()에 의해 표시되었습니다?? 그게 현재 객체입니다(this)! 따라서 수정된 run() 메서드는 다음과 같습니다.

file:// 동기화된 키워드가 없다는 점에 유의하세요
public void run() {
while (true) {
synchronized ( this){
count1++;
count2++;
}
System.out.println("Count1="+count1+",Count2="+count2);
{
수면을 시도해 보세요. (500);
} catch (InterruptedException e){}
}
}

file://Note, synchTest()에는 여전히 동기화 키워드가 필요합니다. 이유를 생각해 보세요.

이 경우 synchTest 메소드를 호출할 수 있으며, accessCount의 변화도 확인할 수 있습니다.

위 내용은 Java 초보자를 위한 메모(7) 내용과 기타 관련 내용입니다. PHP 중국어 홈페이지(www.php.cn)를 주목해주세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.