Java 스레드가 통신하는 방법에 대한 이해에 대해 이야기하십시오.

멀티 스레딩과 동시성은 정상적인 개발에서 일부 소규모 파트너에 의해 많이 사용되지 않지만 일부 업무 경험 인터뷰는 여전히 질문하기 쉽습니다. 따라서 다음 몇 호에서는 참조를 위해 일반적인 멀티 스레드 인터뷰 질문을 정렬할 것입니다. .

의사 소통 방식

①동기화

②투표 중 방식

③대기/통지 메커니즘

④파이프라인 통신

1. 서론
이 글은 JAVA 멀티스레딩에서 쓰레드 간 통신 방식에 대한 나의 이해를 요약한 것으로, 주로 쓰레드 간 통신을 코드와 텍스트를 결합하는 방식으로 다루므로 책에 있는 샘플 코드를 일부 발췌했다.

둘째, 쓰레드간 통신 방식
① 동기화
여기서 말하는 동기화는 동기화된 키워드를 통한 여러 쓰레드 간의 통신을 의미한다.

참조 예:

공개 클래스 MyObject {

synchronized public void methodA() {
    //do something....
}

synchronized public void methodB() {
    //do some other thing
}

}

공개 클래스 ThreadA는 스레드 {를 확장합니다.

private MyObject object;

//생성자 생략
@Override
public void run() { super.run(); object.methodA(); } }



공개 클래스 ThreadB는 스레드 {를 확장합니다.

private MyObject object;

//생성자 생략
@Override
public void run() { super.run(); object.methodB(); } }



공개 클래스 실행 { 공개 정적 무효 메인(String[] 인수) { MyObject 개체 = new MyObject();

    //线程A与线程B 持有的是同一个对象:object
    ThreadA a = new ThreadA(object);
    ThreadB b = new ThreadB(object);
    a.start();
    b.start();
}

}
스레드 A와 스레드 B는 MyObject 클래스의 동일한 개체 개체를 보유하므로 이 두 스레드는 서로 다른 메서드를 호출해야 하지만 동기적으로 실행됩니다. 예를 들어 스레드 B는 스레드 A가 methodA() 메서드 실행을 완료할 때까지 기다려야 합니다. , methodB() 메서드를 실행할 수 있습니다. 이러한 방식으로 스레드 A와 스레드 B가 통신합니다.

이 방법은 본질적으로 "공유 메모리" 유형의 통신입니다. 여러 스레드가 동일한 공유 변수에 액세스해야 하며 잠금(액세스 권한)을 얻은 사람이 이를 실행할 수 있습니다.

②폴링 중 방식
코드는 다음과 같습니다.

가져오기 java.util.ArrayList;
가져오기 java.util.List;

공개 클래스 MyList {

private List<String> list = new ArrayList<String>();
public void add() {
    list.add("elements");
}
public int size() {
    return list.size();
}

}

가져오기 mylist.MyList;

공개 클래스 ThreadA는 스레드 {를 확장합니다.

private MyList list;

public ThreadA(MyList list) {
    super();
    this.list = list;
}

@Override
public void run() {
    try {
        for (int i = 0; i < 10; i++) {
            list.add();
            System.out.println("添加了" + (i + 1) + "个元素");
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

가져오기 mylist.MyList;

공개 클래스 ThreadB는 스레드 {를 확장합니다.

private MyList list;

public ThreadB(MyList list) {
    super();
    this.list = list;
}

@Override
public void run() {
    try {
        while (true) {
            if (list.size() == 5) {
                System.out.println("==5, 线程b准备退出了");
                throw new InterruptedException();
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

가져오기 mylist.MyList;
가져오기 extthread.ThreadA;
가져오기 extthread.ThreadB;

공개 클래스 테스트 {

public static void main(String[] args) {
    MyList service = new MyList();

    ThreadA a = new ThreadA(service);
    a.setName("A");
    a.start();

    ThreadB b = new ThreadB(service);
    b.setName("B");
    b.start();
}

}
이와 같이 쓰레드 A는 계속해서 조건을 변경하고, 쓰레드 B는 while 문을 통해 이 조건(list.size()==5)이 성립하는지 계속 확인하여 쓰레드 간 통신을 구현한다. 그러나 이 방법은 CPU 리소스를 낭비합니다.

자원낭비라고 하는 이유는 JVM 스케줄러가 실행을 위해 CPU를 스레드 B에 넘길 때 '유용한' 작업을 하지 않고 특정 조건이 맞는지 지속적으로 테스트하기 때문이다. 실생활에서와 마찬가지로 누군가가 전화가 오는지 확인하기 위해 계속 전화 화면을 보고 있습니다. 다른 일을 하거나 전화가 오면 벨이 울려 TA에게 전화가 온다는 것을 알립니다.

이 접근 방식에는 또 다른 문제가 있습니다.

폴링 조건의 가시성에 대해서는 메모리 가시성에 대해 다음을 참조하십시오. JAVA 다중 스레딩에서 휘발성과 동기화된 비교의 첫 번째 요점 "하나, 휘발성 키워드의 가시성"

스레드는 먼저 변수를 로컬 스레드 스택 공간으로 읽은 다음 이동하여 수정하는 로컬 변수입니다. 따라서 스레드 B가 매번 로컬 조건 변수를 가져오는 경우 다른 스레드가 폴링 조건을 변경하더라도 이를 인식하지 못하고 무한 루프가 발생합니다. 여기에서는 모든 사람에게 아키텍처 학습 교환 서클을 추천합니다. 커뮤니케이션 스터디 가이드 Pseudo Xin: 1253431195(인터뷰 질문과 답변이 많이 있음), 선임 아키텍트가 녹화한 일부 비디오 녹화 공유: Spring, MyBatis, Netty 소스 코드 분석, 높은 동시성, 고성능, 분산형, 마이크로서비스 아키텍처 JVM 성능 최적화의 원리, 분산 아키텍처 등은 건축가에게 필요한 지식 시스템이 되었습니다. 현재 많은 혜택을 받고 있는 무료 학습 자료도 받을 수 있습니다.

③대기/알림 메커니즘
코드는 다음과 같습니다.

가져오기 java.util.ArrayList;
가져오기 java.util.List;

공개 클래스 MyList {

private static List<String> list = new ArrayList<String>();

public static void add() {
    list.add("anyString");
}

public static int size() {
    return list.size();
}

}

공개 클래스 ThreadA는 스레드 {를 확장합니다.

private Object lock;

public ThreadA(Object lock) {
    super();
    this.lock = lock;
}

@Override
public void run() {
    try {
        synchronized (lock) {
            if (MyList.size() != 5) {
                System.out.println("wait begin "
                        + System.currentTimeMillis());
                lock.wait();
                System.out.println("wait end  "
                        + System.currentTimeMillis());
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

공개 클래스 ThreadB extends Thread { 개인 개체 잠금;

public ThreadB(Object lock) {
    super();
    this.lock = lock;
}

@Override
public void run() {
    try {
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                MyList.add();
                if (MyList.size() == 5) {
                    lock.notify();
                    System.out.println("已经发出了通知");
                }
                System.out.println("添加了" + (i + 1) + "个元素!");
                Thread.sleep(1000);
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

공개 클래스 실행 {

public static void main(String[] args) {

    try {
        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        a.start();

        Thread.sleep(50);

        ThreadB b = new ThreadB(lock);
        b.start();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}
스레드 A는 작업을 수행하기 전에 특정 조건(list.size()==5)이 충족될 때까지 기다립니다. 스레드 B는 목록에 요소를 추가하고 목록의 크기를 변경합니다.

A와 B는 어떻게 의사 소통합니까? 즉, 스레드 A는 list.size()가 이미 5라는 것을 어떻게 알 수 있습니까?

여기서는 Object 클래스의 wait() 및 notify() 메서드를 사용합니다.

조건이 충족되지 않으면(list.size() !=5) 스레드 A는 wait()를 호출하여 CPU를 포기하고 차단 상태로 들어갑니다. - 폴링 시 ②처럼 CPU를 차지하지 않음

조건이 충족되면 스레드 B는 notify()를 호출하여 스레드 A에 알립니다. 소위 알림 스레드 A는 스레드 A를 깨우고 실행 가능한 상태로 들어가게 하는 것입니다.

이 접근 방식의 이점 중 하나는 CPU 사용률이 증가한다는 것입니다.

그러나 몇 가지 단점도 있습니다. 예를 들어 스레드 B가 먼저 실행되고 한 번에 5개의 요소를 추가하고 알림을 보내기 위해 notify()를 호출하는 반면 스레드 A는 이 시간에 여전히 실행 중입니다. 그러면 결코 깨어날 수 없습니다. 스레드 B는 이미 알림을 보냈고 앞으로 알림을 보내지 않을 것입니다. 이는 알림이 너무 이르므로 프로그램의 실행 논리를 방해할 수 있음을 나타냅니다.

④Pipe 통신
은 java.io.PipedInputStream과 java.io.PipedOutputStream을 사용하여 통신하는 것입니다.

자세히 소개하지 않습니다. 분산 시스템에는 공유 메모리 메커니즘과 메시지 통신 메커니즘의 두 가지 통신 메커니즘이 있습니다. 나는 이전 ①의 동기화된 키워드와 ②의 동안 폴링이 공유 메모리 메커니즘에 "속한다"고 생각합니다. 왜냐하면 폴링 조건이 volatile 키워드로 수정될 때 이 "공유 조건 변수"에 대한 판단을 통과한다는 것을 의미하기 때문입니다 프로세스 간 통신을 달성하기 위해 변경되었는지 여부입니다.

파이프라인 통신은 메시지 전달 메커니즘에 가깝습니다. 즉, 한 스레드의 메시지가 파이프를 통해 다른 스레드로 전송됩니다.

Guess you like

Origin blog.csdn.net/m0_54828003/article/details/127260365