40 개의 고주파 자바 동시 프로그래밍 인터뷰 질문 분석

1. 멀티 스레딩의 용도는 무엇입니까?

1) 멀티 코어 CPU 활용

산업이 발전함에 따라 현재의 노트북, 데스크탑 및 심지어 상용 애플리케이션 서버도 적어도 듀얼 코어이고 4 코어, 8 코어 또는 16 코어도 드물지 않습니다. 단일 스레드 프로그램 인 경우에는 듀얼 코어 CPU는 50 %, 4 코어 CPU는 75 %가 낭비되었습니다. 단일 코어 CPU의 소위 "멀티 스레딩"은 가짜 멀티 스레딩입니다. 프로세서는 동시에 논리 조각 만 처리 할 수 ​​있지만 스레드가 더 빠르게 전환되고 여러 스레드가 "동시에"실행되는 것처럼 보입니다. 멀티 코어 CPU의 멀티 스레딩은 실제 멀티 스레딩입니다.이를 통해 멀티 세그먼트 로직이 동시에 작동 할 수 있습니다. 멀티 스레딩은 멀티 코어 CPU를 진정으로 활용하고 전체를 만드는 목적을 달성 할 수 있습니다. CPU 사용.

2) 차단 방지

프로그램 효율성의 관점에서 단일 코어 CPU는 멀티 스레딩의 이점을 충분히 활용하지 못할뿐만 아니라 단일 코어 CPU에서 멀티 스레딩을 실행하기 때문에 스레드 컨텍스트를 전환하여 전체 효율성을 떨어 뜨립니다. 프로그램. 그러나 단일 코어 CPU의 경우 차단을 방지하기 위해 여전히 멀티 스레딩을 사용해야합니다. 단일 코어 CPU가 단일 스레드를 사용하는 경우 스레드가 차단되어있는 한 (예 : 특정 데이터를 원격으로 읽는 경우) 피어가 반환되지 않고 시간 제한을 설정하지 않은 경우 전체 프로그램이 이전에있을 것입니다. 데이터가 반환됩니다. 실행이 중지되었습니다. 멀티 스레딩은이 문제를 방지 할 수 있으며, 여러 스레드가 동시에 실행되고있어 데이터 읽기를 위해 한 스레드의 코드 실행이 차단 되어도 다른 작업의 실행에는 영향을 미치지 않습니다.

3) 모델링 용이

이것은 분명하지 않은 또 다른 장점입니다. 큰 작업 A, 단일 스레드 프로그래밍이 있다고 가정하면 많은 고려 사항이 있고 전체 프로그램 모델을 구축하는 것이 번거 롭습니다. 그러나이 큰 작업 A를 여러 개의 작은 작업 인 작업 B, 작업 C 및 작업 D로 분해하고 프로그램 모델을 별도로 구축하고 이러한 작업을 멀티 스레딩을 통해 개별적으로 실행하면 훨씬 간단합니다.

2. 스레드를 만드는 방법

보다 일반적인 문제는 일반적으로 두 가지 유형이 있습니다.

1) Thread 클래스 상속

2) Runnable 인터페이스 구현

인터페이스를 구현하는 방법이 클래스 상속 방법보다 유연하고 프로그램 간의 결합을 줄일 수 있기 때문에 후자가 더 낫다는 것은 말할 필요도 없습니다. 인터페이스 지향 프로그래밍도 핵심입니다. 디자인 패턴의 6 가지 원칙 중.

3. start () 메서드와 run () 메서드의 차이점

start () 메서드가 호출 될 때만 멀티 스레딩의 특성을 보여줄 수 있으며 다른 스레드의 run () 메서드에있는 코드가 번갈아 실행됩니다. run () 메서드를 호출하기 만하면 코드가 동 기적으로 실행되고 다른 스레드가 run () 메서드에서 코드를 실행하기 전에 한 스레드의 run () 메서드에있는 코드가 실행이 완료 될 때까지 기다려야합니다.

4. Runnable 인터페이스와 Callable 인터페이스의 차이점

약간의 심오한 질문이 있으며 Java 프로그래머에 대한 지식의 폭도 있습니다.

Runnable 인터페이스에서 run () 메서드의 반환 값은 void입니다. 순전히 run () 메서드에서 코드를 실행하는 것입니다. Callable 인터페이스의 call () 메서드는 반환 값을 가지며 일반 유형입니다. , 및 Future, FutureTask를 사용하여 비동기 실행 결과를 얻을 수 있습니다.

멀티 스레딩은 단일 스레드보다 더 어렵고 복잡하기 때문에 실제로 이것은 매우 유용한 기능입니다. 중요한 이유는 멀티 스레딩이 알려지지 않은 항목으로 가득 차 있기 때문입니다. 스레드가 실행 되었습니까? 스레드는 얼마나 오래 실행 되었습니까? 스레드가 실행될 때 예상되는 데이터가 할당 되었습니까? 우리가 할 수있는 것은이 다중 스레드 작업이 완료 될 때까지 기다리는 것뿐이라는 것을 아는 것은 불가능합니다. Callable + Future / FutureTask는 다중 스레드 실행 결과를 얻을 수 있으며 대기 시간이 너무 길고 필요한 데이터를 얻지 못한 경우 스레드 작업을 취소하는 것이 정말 유용합니다.

5. CyclicBarrier와 CountDownLatch의 차이점

java.util.concurrent 아래에서 겉보기에 유사한 두 클래스를 사용하여 코드가 특정 지점까지 실행됨을 나타낼 수 있습니다. 둘의 차이점은 다음과 같습니다.

1) CyclicBarrier의 스레드가 특정 지점까지 실행 된 후 모든 스레드가이 지점에 도달 할 때까지 스레드가 실행을 중지하고 모든 스레드가 다시 실행됩니다 .CountDownLatch는 스레드가 특정 지점까지 실행 된 후 특정 값을 제공하지 않습니다. -1이면 스레드가 계속 실행됩니다.

2) CyclicBarrier는 하나의 작업 만 불러 낼 수 있고 CountDownLatch는 여러 작업을 불러 낼 수 있습니다.

  1. CyclicBarrier는 다시 사용할 수 있고 CountDownLatch는 다시 사용할 수 없으며 카운트 값이 0 인 CountDownLatch는 더 이상 사용할 수 없습니다.

6, 휘발성 키워드의 역할

매우 중요한 문제는 멀티 스레딩을 배우고 적용하는 모든 Java 프로그래머가이를 마스터해야한다는 것입니다. volatile 키워드의 역할을 이해하기위한 전제 조건은 Java 메모리 모델을 이해하는 것입니다. Java 메모리 모델은 여기에서 설명하지 않습니다. 31 번 항목을 참조 할 수 있습니다. volatile 키워드에는 두 가지 주요 기능이 있습니다.

1) 멀티 스레딩은 가시성과 원자 성의 두 가지 특성에 주로 초점을 맞추고 있으며, volatile 키워드로 수정 된 변수는 여러 스레드간에 가시성을 보장합니다. 즉, 휘발성 변수를 읽을 때마다 최신 데이터 여야합니다.

2) 기본 코드 실행은 Java 프로그램에서 본 고급 언어만큼 간단하지 않습니다. 실행은 Java 코드-> 바이트 코드-> 바이트 코드에 따라 해당 C / C ++ 코드 실행-> C / C ++ 코드는 하드웨어 회로와 상호 작용하기 위해 어셈블리 언어로 컴파일됩니다. 실제로 JVM은 더 나은 성능을 얻기 위해 명령을 재정렬 할 수 있으며 멀티 스레딩에서 예기치 않은 문제가 발생할 수 있습니다. volatile을 사용하면 금지 된 의미 체계가 재정렬되므로 물론 코드 실행 효율성이 어느 정도 감소합니다.

실용적인 관점에서 볼 때 volatile의 중요한 기능은 원 자성을 보장하기 위해 CAS와 결합하는 것입니다. 자세한 내용은 AtomicInteger와 같은 java.util.concurrent.atomic 패키지의 클래스를 참조하십시오.

7. 스레드 안전성이란?

또 다른 이론적 질문입니다. 다양한 답변이 있습니다. 개인적으로 가장 좋은 설명은 다음과 같습니다. 코드가 여러 스레드에서 실행되고 단일 스레드에서 실행되면 항상 동일한 결과를 얻을 수 있습니다. 그러면 코드가 스레드로부터 안전합니다.

이 문제는 언급 할 가치가 있습니다. 즉, 여러 수준의 스레드 안전성이 있습니다.

1) 불변

String, Integer, Long과 마찬가지로 모두 최종 유형입니다. 스레드는 값을 변경할 수 없습니다. 새로 생성하지 않는 한 변경해야합니다. 따라서 이러한 불변 객체는 동기화 수단없이 멀티 스레드 환경에서 직접 사용할 수 있습니다.

2) 절대적인 스레드 안전성

런타임 환경에 관계없이 호출자는 추가 동기화 조치가 필요하지 않습니다. 이렇게하려면 일반적으로 많은 추가 비용이 필요합니다. Java는 자체적으로 스레드 안전 클래스를 호출합니다. 실제로 대부분은 스레드로부터 안전하지 않습니다. 그러나 Java에는 CopyOnWriteArrayList 및 CopyOnWriteArraySet과 같은 절대적으로 스레드로부터 안전한 클래스가 있습니다.

3) 상대적 스레드 안전성

상대 스레드 안전성은 일반적인 의미에서 스레드 안전성이라고 부르는 것입니다. Vector와 마찬가지로 add 및 remove 메서드는 모두 원자 적 작업이며 중단되지 않고 이에 대해서만 중단됩니다. 스레드가 Vector를 순회하는 경우 스레드가 있습니다. 이 Vector를 동시에 추가하면 99 %의 케이스가 ConcurrentModificationException으로 표시됩니다. 이는 fail-fast 메커니즘입니다.

4) 스레드가 안전하지 않습니다.

이에 대해 말할 것도없고 ArrayList, LinkedList, HashMap 등은 모두 스레드에 안전하지 않은 클래스입니다.

8. 자바에서 스레드 덤프 파일을 얻는 방법

무한 루프, 교착 상태, 차단, 느린 페이지 열기 등과 같은 문제의 경우 스레드 덤프가 문제를 해결하는 가장 좋은 방법입니다. 소위 스레드 덤프는 스레드 스택입니다. 스레드 스택을 얻으려면 두 단계가 있습니다.

1) 스레드의 pid를 얻으려면 jps 명령을 사용하거나 Linux 환경에서 ps -ef | grep java를 사용할 수 있습니다.

2) 스레드 스택을 인쇄하려면 jstack pid 명령을 사용할 수 있으며 Linux 환경에서는 kill -3 pid를 사용할 수도 있습니다.

또 다른 요점은 Thread 클래스가 스레드 스택을 가져 오는 데 사용할 수도있는 getStackTrace () 메서드를 제공한다는 것입니다. 이것은 인스턴스 메서드이므로이 메서드는 특정 스레드 인스턴스에 바인딩되고 현재 실행중인 특정 스레드의 스택을 가져올 때마다 바인딩됩니다.

9. 스레드에 런타임 예외가 있으면 어떻게됩니까?

예외가 발견되지 않으면 스레드 실행이 중지됩니다. 또 다른 중요한 점은이 스레드가 특정 개체의 모니터를 보유하면 개체 모니터가 즉시 해제된다는 것입니다.

10. 두 스레드간에 데이터를 공유하는 방법

스레드간에 개체를 공유 한 다음 wait / notify / notifyAll, await / signal / signalAll을 통해 호출하고 대기하는 것으로 충분합니다. 예를 들어 차단 큐 BlockingQueue는 스레드간에 데이터를 공유하도록 설계되었습니다.

11. 수면 방식과 대기 방식의 차이점은 무엇입니까?

이 질문은 자주 묻는 질문입니다. sleep 방식과 wait 방식 모두 일정 시간 동안 CPU를 포기하는 데 사용할 수 있습니다. 차이점은 스레드가 객체의 모니터를 보유하면 sleep 방식이 포기하지 않는다는 것입니다. 이 개체의 모니터, 대기 메소드는 이것을 포기합니다.

12. 생산자 소비자 모델의 역할은 무엇입니까

이 질문은 매우 이론적이지만 매우 중요합니다.

1) 생산자의 생산 능력과 소비자의 소비 능력의 균형을 맞추어 전체 시스템의 운영 효율성을 향상시키는 것이 생산자-소비자 모델의 가장 중요한 역할

2) 생산자-소비자 모델의 부수적 효과 인 디커플링 디커플링은 생산자와 소비자 간의 연결이 적고 연결이 적을수록 상호 제약없이 독립적으로 발전 할 수 있음을 의미합니다.

13, ThreadLocal의 용도는 무엇입니까

간단히 말해 ThreadLocal은 시간에 따라 공간을 바꾸는 방식으로, Open Address 방식으로 구현 된 ThreadLocal.ThreadLocalMap은 각 Thread에서 유지되며 데이터는 분리되고 데이터는 공유되지 않으며 당연히 Thread Safety 문제는 없습니다.

14. 동기화 된 블록에서 wait () 메서드와 notify () / notifyAll () 메서드를 호출해야하는 이유

이것은 JDK에서 필수입니다. wait () 메소드와 notify () / notifyAll () 메소드는 호출하기 전에 객체 잠금을 획득해야합니다.

15. 객체 모니터를 포기할 때 wait () 메서드와 notify () / notifyAll () 메서드의 차이점은 무엇입니까?

개체 모니터를 포기할 때 wait () 메서드와 notify () / notifyAll () 메서드의 차이점은 다음과 같습니다. wait () 메서드는 개체 모니터를 즉시 해제하고 notify () / notifyAll () 메서드는 실행을 완료하는 스레드의 나머지 코드는 개체 모니터를 포기합니다.

16. 스레드 풀을 사용하는 이유

스레드의 빈번한 생성 및 파괴를 피하고 스레드 객체를 재사용하십시오. 또한 스레드 풀을 사용하여 동시 프로젝트 수를 유연하게 제어 할 수도 있습니다.

17. 스레드가 개체 모니터를 보유하고 있는지 감지하는 방법

또한 스레드가 개체 모니터를 보유하고 있는지 여부를 결정하는 방법이 있음을 확인하기 위해 인터넷에서 다중 스레드 인터뷰 질문을 보았습니다. Thread 클래스는 개체의 모니터가있는 경우에만 holdLock (Object obj) 메서드를 제공합니다. obj is 스레드가 보유 할 때 true를 반환합니다. 이것은 "스레드"가 현재 스레드를 참조 함을 의미하는 정적 메서드입니다.

18. 동기화와 재진입 잠금의 차이점

Synchronized는 if, else, for, while과 동일한 키워드이며 ReentrantLock은 둘의 본질적인 차이점 인 클래스입니다. ReentrantLock은 클래스이므로 동기화 된 것보다 더 유연한 기능을 제공합니다. 상속 될 수 있고 메서드를 가질 수 있으며 다양한 클래스 변수를 가질 수 있습니다. ReentrantLock은 다음과 같은 여러 측면에서 동기화 된 것보다 더 확장 가능합니다.

(1) ReentrantLock은 교착 상태를 피하기 위해 잠금 획득 대기 시간을 설정할 수 있습니다.

(2) ReentrantLock은 다양한 잠금 정보를 얻을 수 있습니다.

(3) ReentrantLock은 여러 알림을 유연하게 구현할 수 있습니다.

또한이 둘의 잠금 메커니즘은 실제로 다릅니다. ReentrantLock의 맨 아래 계층은 Unsafe park 메서드를 호출하여 잠그고 동기화 된 작업은 개체 헤더의 표시 단어 여야합니다.

19. ConcurrentHashMap의 동시성은 무엇입니까

ConcurrentHashMap의 동시성은 세그먼트의 크기입니다. 기본값은 16이며, 이는 최대 16 개의 스레드가 동시에 ConcurrentHashMap을 운영 할 수 있음을 의미합니다. 이는 Hashtable에 대한 ConcurrentHashMap의 가장 큰 장점이기도합니다. 어떤 경우에도 Hashtable은 두 가지를 가질 수 있습니다. 동시에 Hashtable을 얻기위한 스레드 데이터?

20, ReadWriteLock이란?

우선 ReentrantLock이 나쁘다는 것은 아니지만 ReentrantLock에는 때때로 제한이 있습니다. ReentrantLock을 사용하는 경우, 쓰레드 A가 데이터를 쓰고 쓰레드 B가 데이터를 읽음으로써 발생하는 데이터 불일치를 방지하기위한 것일 수 있지만, 이런 식으로 쓰레드 C가 데이터를 읽고 있다면 쓰레드 D도 데이터를 읽고있어 데이터를 읽는 것은 데이터를 변경하지 않습니다. 데이터, 잠금이 필요하지 않지만 여전히 잠금 상태이므로 프로그램의 성능이 저하됩니다.

이 때문에 ReadWriteLock이 탄생했습니다. ReadWriteLock은 읽기-쓰기 잠금 인터페이스입니다. ReentrantReadWriteLock은 읽기와 쓰기의 분리를 실현하는 ReadWriteLock 인터페이스의 특정 구현입니다. 읽기 잠금은 공유되고 쓰기 잠금은 배타적입니다. 읽기와 쓰기 사이에 상호 배제가 없습니다. 쓰기 및 읽기, 쓰기 및 쓰기는 상호 배타적이므로 읽기 및 쓰기 성능이 향상됩니다.

21. FutureTask 란?

이것은 실제로 앞에서 언급했듯이 FutureTask는 비동기 작업 작업을 나타냅니다. Callable의 특정 구현 클래스는 FutureTask로 전달 될 수 있습니다.이 클래스는이 비동기 작업 작업의 결과를 얻기 위해 대기하고 완료되었는지 여부를 확인하고 작업을 취소 할 수 있습니다. 물론 FutureTask는 Runnable 인터페이스의 구현 클래스이기 때문에 FutureTask는 스레드 풀에 배치 할 수도 있습니다.

22. Linux 환경에서 가장 긴 CPU를 사용하는 스레드를 찾는 방법

이것은 더 실용적인 문제이고, 이런 종류의 문제는 상당히 의미가 있다고 생각합니다. 다음과 같이 할 수 있습니다.

(1) 앞서 언급했듯이 프로젝트의 pid, jps 또는 ps -ef | grep java를 가져옵니다.

(2) top -H -p pid, 순서는 변경할 수 없습니다.

이러한 방식으로 현재 프로젝트와 각 스레드가 차지하는 CPU 시간 비율을 인쇄 할 수 있습니다. 여기에 입력 된 것은 운영 체제의 기본 스레드 스레드 번호 인 LWP입니다. 내 랩톱은 Linux 환경에서 Java 프로젝트를 배포하지 않으므로 시연 할 스크린 샷을 찍을 방법이 없습니다. 회사는 Linux 환경을 사용하여 프로젝트를 배포 할 수 있습니다.

"top -H -p pid"+ "jps pid"를 사용하면 높은 CPU를 차지하는 스레드의 스레드 스택을 쉽게 찾을 수 있으므로 CPU 점유율이 높은 원인을 찾을 수 있습니다. 일반적으로 잘못된 코드 작업은 무한 루프로 이어집니다. .

마지막으로 "top -H -p pid"로 입력 한 LWP는 10 진수이고, "jps pid"로 입력 한 로컬 스레드 번호는 16 진수입니다. 변환 후 높은 CPU를 차지하는 스레드를 찾을 수 있습니다. 현재 스레드 스택은 다음과 같습니다. 쪽으로.

23, Java 프로그래밍은 교착 상태를 일으키는 프로그램을 작성합니다.

이 주제를 처음 보았을 때 매우 좋은 질문이라고 생각했습니다. 많은 사람들이 교착 상태가 무엇인지 알고 있습니다. 스레드 A와 스레드 B는 서로의 잠금을 기다려 프로그램이 끝없이 반복됩니다. 물론 이것으로 제한되어 있습니다. 교착 상태 프로그램을 작성하는 방법을 모르겠습니다.이 경우 교착 상태가 무엇인지 이해하지 못합니다. 이론을 이해하면 끝났습니다. 실제로 교착 상태가 발생합니다. 기본적으로는 보이지 않습니다.

교착 상태가 무엇인지 진정으로 이해하기 위해이 문제는 어렵지 않고 몇 단계 만 거치면됩니다.

1) 두 스레드는 lock1과 lock2라는 두 개의 Object 객체를 보유합니다. 이 두 잠금은 동기화 코드 블록의 잠금으로 사용됩니다.

2) 스레드 1의 run () 메서드의 동기화 코드 블록은 먼저 lock1, Thread.sleep (xxx)의 객체 잠금을 획득하고 시간이 많이 걸리지 않고 50 밀리 초가 거의 동일한 다음 객체 잠금을 획득합니다. lock2의. 이것의 주된 목적은 스레드 1이 lock1 및 lock2의 객체 잠금을 한 번에 획득하지 못하도록하는 것입니다.

3) 스레드 2 실행) (메소드의 동기화 코드 블록은 먼저 lock2의 객체 잠금을 획득 한 다음 lock1의 객체 잠금을 획득합니다. 물론 lock1의 객체 잠금은 스레드 1 잠금에 의해 유지되고 스레드 2는 스레드 1을 기다려야합니다. lock1의 객체 잠금을 해제합니다.

이와 같이 쓰레드 1은 휴면 후 "휴면"되고, 쓰레드 2는 lock2의 객체 잠금을 획득하고, 스레드 1은 이때 lock2의 객체 잠금을 획득하려고 시도하여 차단됩니다. 이때 교착 상태가 형성됩니다. . 코드가 작성되지 않았고 많은 공간을 차지함 Java Multithreading 7 : Deadlock이 기사에는 위 단계의 코드 구현이 포함되어 있습니다.

24. 차단 된 스레드를 깨우는 방법

wait (), sleep () 또는 join ()을 호출하여 스레드가 차단 된 경우 InterruptedException을 발생시켜 스레드를 중단하고 깨울 수 있습니다. 스레드가 IO 차단을 만나면 할 수있는 것이 없습니다. 운영 체제 실현, Java 코드가 운영 체제에 직접 닿을 수있는 방법은 없습니다.

25. 불변 객체는 멀티 스레딩을 어떻게 도와 주는가

앞서 언급 한 문제는 불변 객체가 객체의 메모리 가시성을 보장하고 불변 객체를 읽는 데 추가 동기화 수단이 필요하지 않아 코드 실행 효율성이 향상된다는 것입니다.

26. 다중 스레드 컨텍스트 전환이란?

다중 스레드 컨텍스트 전환은 CPU 제어가 이미 실행중인 스레드에서 CPU 실행 권한을 얻기 위해 대기중인 다른 스레드로 전환되는 프로세스를 나타냅니다.

27. 작업을 제출할 때 스레드 풀 대기열이 가득 차면 어떻게됩니까?

여기에서 구별하십시오.

1) 제한되지 않은 대기열 LinkedBlockingQueue, 즉 제한되지 않은 대기열을 사용하는 경우 LinkedBlockingQueue는 작업을 저장할 수있는 무한 대기열로 간주 될 수 있으므로 계속해서 차단 대기열에 작업을 추가하여 실행을 기다립니다. 무한히

2) ArrayBlockingQueue와 같은 바운드 큐를 사용하는 경우 먼저 ArrayBlockingQueue에 태스크가 추가됩니다. ArrayBlockingQueue가 가득 차면 maximumPoolSize 값에 따라 스레드 수가 증가합니다. 스레드 수가 증가하면 스레드 수가 증가합니다. , 스레드 수를 처리 할 수없고 ArrayBlockingQueue가 계속 가득 차면 거부 정책 RejectedExecutionHandler를 사용하여 전체 작업을 처리합니다. 기본값은 AbortPolicy입니다.

28. Java에서 사용되는 스레드 스케줄링 알고리즘은 무엇입니까?

선점. 스레드에 CPU가 부족한 후 운영 체제는 스레드 우선 순위, 스레드 부족 및 기타 데이터를 기반으로 전체 우선 순위를 계산하고 실행을 위해 다음 시간 조각을 스레드에 할당합니다.

29, Thread.sleep (0)의 역할은 무엇입니까?

이 질문은 위의 질문과 관련이 있으며 연결되어 있습니다. Java는 선점 형 스레드 스케줄링 알고리즘을 사용하기 때문에 스레드가 CPU를 제어하는 ​​경우가 자주 발생할 수 있습니다. 우선 순위가 낮은 일부 스레드가 CPU를 제어 할 수 있도록하려면 Thread.sleep (0) 수동 트리거를 사용할 수 있습니다. 시간 조각을 할당하는 운영 체제의 작업이며 CPU 제어의 균형을 맞추는 작업이기도합니다.

30. 스핀이란?

동기화 된 많은 코드는 단순한 코드 일 뿐이며 실행 시간이 매우 빠르며, 이때 스레드 차단은 사용자 모드와 커널 모드 사이의 전환을 포함하기 때문에 대기중인 스레드가 잠겨있는 작업은 가치가 없을 수 있습니다. 동기화 된 코드는 매우 빠르게 실행되기 때문에 잠금을 기다리는 스레드가 차단되지 않도록하고 동기화 된 경계에서 바쁜 루프를 수행합니다. 여러 번의 사용 중 루프를 수행하고 잠금을 얻지 못한 경우 다시 차단하면 더 나은 전략이 될 수 있습니다.

31. 자바 메모리 모델이란?

Java 메모리 모델은 Java 메모리에 대한 다중 스레드 액세스에 대한 사양을 정의합니다. Java 메모리 모델에 대한 완전한 설명은 여기서 몇 마디로 명확하게 설명 할 수있는 것이 아닙니다. Java 메모리 모델의 여러 부분을 간략하게 요약하겠습니다.

1) 자바 메모리 모델은 메모리를 주 메모리와 작업 메모리로 나눕니다. 클래스의 상태, 즉 클래스간에 공유되는 변수는 메인 메모리에 저장됩니다 .Java 스레드가 메인 메모리에서 이러한 변수를 사용할 때마다 메인 메모리의 변수를 한 번 읽고 이러한 메모리가 존재하도록합니다. 작업 메모리에 복사본이 있습니다. 자신의 스레드 코드를 실행할 때 이러한 변수를 사용하고 작업 메모리에서 복사본을 조작합니다. 스레드 코드가 실행 된 후 최신 값이 메인 메모리에 업데이트됩니다.

2) 주 메모리 및 작업 메모리의 변수를 조작하기 위해 몇 가지 원자 연산이 정의됩니다.

3) 휘발성 변수 사용 규칙 정의

4) 이전에 발생, 즉 첫 번째 발생 원칙으로, 작업 A가 작업 B보다 먼저 발생해야하는 일부 규칙을 정의합니다. 예를 들어, 동일한 스레드에서 제어 흐름 앞에있는 코드는 뒤에있는 코드보다 먼저 발생해야합니다. 제어 흐름 및 잠금 해제 잠금 해제 동일한 잠금 등에 대한 잠금 잠금 작업 전에 작업이 발생해야합니다. 이러한 규칙이 충족되는 한 추가 동기화 조치가 필요하지 않습니다. 코드가 모두를 충족하지 않는 경우 발생합니다. -규칙 이전에이 코드는 스레드에 안전하지 않아야합니다.

32. CAS 란?

CAS에서 전체 이름은 Compare and Swap, 즉 compare-replace입니다. 세 개의 피연산자가 있다고 가정합니다 : 메모리 값 V, 이전 예상 값 A 및 수정 될 값 B. 예상 값 A와 메모리 값 V가 동일한 경우에만 메모리 값이 B로 수정됩니다. true를 반환하고 그렇지 않으면 what을 수행하지 않고 false를 반환합니다. 물론 CAS는 휘발성 변수와 협력하여 매번 얻은 변수가 주 메모리의 최신 값인지 확인해야합니다. 그렇지 않으면 이전 예상 값 A는 항상 특정 스레드에 대한 상수 값 A가됩니다. CAS 작업이 실패하면 성공하지 못합니다.

33. 낙관적 잠금과 비관적 잠금이란?

1) 낙관적 잠금 : 이름과 마찬가지로 동시 작업으로 인한 스레드 안전성 문제에 대해 낙관적입니다. 낙관적 잠금은 항상 경쟁이 발생하는 것은 아니라고 믿기 때문에 잠금을 유지할 필요가 없습니다.이 둘을 비교-교체 As an 원자 적 연산 인 경우 액션은 메모리의 변수를 수정하려고 시도하지만 실패하면 충돌이 있음을 의미하며 해당하는 재시도 논리가 있어야합니다.

2) 비관적 잠금 : 이름과 마찬가지로 동시 작업으로 인한 스레드 안전성 문제에 대해 비관적입니다. 비관적 잠금은 경쟁이 항상 발생한다고 믿기 때문에 리소스가 작동 할 때마다 독점 잠금을 보유하고 동기화 된 것과 같습니다. 상황에 관계없이 잠금이 설정된 직후에 리소스를 작동 할 수 있습니다.

34. AQS 란?

AQS에 대해 간략히 설명하면 AQS의 전체 이름은 AbstractQueuedSychronizer이며 추상 큐 동기화 장치로 번역되어야합니다.

java.util.concurrent의 기본이 CAS이면 AQS는 전체 Java 동시 패키지의 핵심이며 ReentrantLock, CountDownLatch, Semaphore 등에서 사용됩니다. AQS는 실제로 양방향 대기열 형태로 모든 항목을 연결합니다. 예를 들어 ReentrantLock과 같이 대기중인 모든 스레드는 항목에 배치되고 양방향 대기열에 연결됩니다. 이전 스레드가 ReentrantLock을 사용하는 경우 양방향 대기열은 다음과 같습니다. 실제로 첫 번째 항목이 실행되기 시작합니다.

AQS는 양방향 대기열에서 모든 작업을 정의하지만 개발자가 사용할 수 있도록 tryLock 및 tryRelease 메서드 만 엽니 다. 개발자는 자신의 구현에 따라 tryLock 및 tryRelease 메서드를 다시 작성하여 자신의 동시 기능을 구현할 수 있습니다.

35. 싱글 톤 모드의 스레드 안전성

가장 흔한 질문은 싱글 톤 모드의 스레드 안전성이 특정 클래스의 인스턴스가 다중 스레드 환경에서 한 번만 생성된다는 것을 의미한다는 것입니다. 싱글 톤 모드를 작성하는 방법에는 여러 가지가 있습니다. 요약하겠습니다.

1) 배고픈 중국식 싱글 톤 패턴 쓰기 : 스레드 안전성

2) 게으른 스타일의 싱글 톤 패턴 쓰기 : 스레드로부터 안전하지 않음

3) 이중 체크 잠금 싱글 톤 모드의 쓰기 : 스레드 안전성

36. Semaphore의 역할은 무엇입니까

세마포어는 세마포어이며 그 역할은 동시 코드 블록 수를 제한하는 것입니다. 세마포어에는 정수 n을 전달할 수있는 생성자가 있습니다. 즉, 특정 코드 조각은 최대 n 개의 스레드에서만 액세스 할 수 있습니다. n을 초과하면 특정 스레드가이 코드 블록 실행을 완료 할 때까지 기다리십시오. 다음 스레드 다시 입력하십시오. int 형 정수 n = 1이 Semaphore 생성자에 전달되면 동기화되는 것과 동일 함을 알 수 있습니다.

37. Hashtable의 size () 메소드에 "return count"라는 문장이 하나 밖에 없는데 왜 동기화가 필요한가요?

이것은 이전의 혼란입니다.이 문제에 대해 생각했는지 모르겠습니다. 한 메서드에 여러 문이 있고 모두 동일한 클래스 변수에서 작동하는 경우 다중 스레드 환경에서 잠금이 발생하지 않으면 필연적으로 스레드 안전 문제가 발생합니다. 이는 이해하기 쉽지만 size () 메서드에는 한 문장, 왜 우리는 잠글 필요가 있습니까?

천천히 일하고 공부하면서이 문제를 이해하는 두 가지 주된 이유가 있습니다.

1) 고정 된 클래스의 동기화 된 메서드는 한 스레드 만 동시에 실행할 수 있지만, 동기화되지 않은 클래스의 메서드는 여러 스레드가 동시에 액세스 할 수 있습니다. 따라서 A 스레드가 데이터를 추가하기 위해 Hashtable의 put 메서드를 실행하고있을 수 있으며, 스레드 B는 일반적으로 size () 메서드를 호출하여 Hashtable의 현재 요소 수를 읽을 수 있으며 읽은 값이 최신 스레드 A가 데이터 추가를 완료했을 수 있지만 스레드 B는 이미 size ++와 일치하지 않고 크기를 읽었으므로 스레드 B가 읽는 크기가 정확하지 않아야합니다. size () 메서드에 동기화를 추가 한 후에는 스레드 A가 put 메서드를 호출 한 후에 만 ​​스레드 B가 size () 메서드를 호출하여 스레드 안전을 보장합니다.

2) CPU는 코드를 실행하지만 Java 코드가 아니므로 매우 중요하며 기억해야합니다. Java 코드는 궁극적으로 실행을 위해 기계 코드로 변환되며 기계 코드는 하드웨어 회로와 실제로 상호 작용할 수있는 코드입니다. Java 코드가 한 줄 뿐이고 Java 코드가 컴파일 된 후 생성 된 바이트 코드가 한 줄만있는 것을 보아도이 문장의 작업이 한 줄뿐이라는 것을 의미하지는 않습니다. 하단 레이어. "return count"의 한 문장은 실행을 위해 3 개의 어셈블리 문장으로 번역되고, 하나의 어셈블리 문장은 기계 코드에 해당하며, 첫 번째 문장이 실행 된 후 스레드가 전환되는 것은 전적으로 가능합니다.

38. 스레드 클래스 생성 방법과 정적 블록은 어느 스레드에서 호출되는지

이것은 매우 까다 롭고 교활한 질문입니다. 기억하세요 : 스레드 클래스의 구성 메서드와 정적 블록은 스레드 클래스 new가있는 스레드에 의해 호출되는 반면 run 메서드의 코드는 스레드 자체에 의해 호출됩니다.

위의 문장이 혼란 스러우면 예를 들어 보겠습니다 .Thread1이 Thread2에서 새롭고 Thread2가 주 함수에서 새롭다 고 가정합니다.

1) Thread2의 구성 메서드와 정적 블록은 메인 스레드에서 호출하고 Thread2의 run () 메서드는 Thread2 자체에서 호출합니다.

2) Thread1의 구성 메서드와 정적 블록은 Thread2에서 호출하고 Thread1의 run () 메서드는 Thread1 자체에서 호출합니다.

39. 동기화 방법과 동기화 블록의 더 나은 선택

동기 블록 : 동기 블록 외부의 코드가 비동기 적으로 실행됨을 의미하며, 이는 전체 동기화 방법보다 코드의 효율성을 향상시킵니다. 원칙을 알고 계십시오. 동기화 범위가 작을수록 좋습니다.

이 기사에서 한 가지 더 언급하고 싶습니다만, 동기화 범위가 작을수록 더 좋기는하지만 Java 가상 머신에는 여전히 동기화 범위를 확대하는 잠금 조 대화라는 최적화 방법이 있습니다. 예를 들어 StringBuffer는 스레드로부터 안전한 클래스입니다. 당연히 가장 일반적으로 사용되는 append () 메소드는 동기화 메소드입니다. 코드를 작성할 때 문자열을 반복적으로 추가하므로 잠금이 반복됩니다.-> Unlock, 이는 Java 가상 머신이이 스레드에서 커널 모드와 사용자 모드 사이를 반복적으로 전환해야하므로 Java 가상 머신이 여러 추가 메소드 호출의 코드를 잠그기 때문에 성능에 악영향을 미칩니다. 거친 작업은 여러 추가 작업을 다음으로 확장합니다. 추가 방법의 시작과 끝은 큰 동기화 블록이되어 잠금-> 잠금 해제 수를 줄이고 코드 실행의 효율성을 효과적으로 향상시킵니다.

40. 동시성이 높고 작업 실행 시간이 짧은 비즈니스에 스레드 풀을 사용하는 방법은 무엇입니까? 동시성이 낮고 작업 실행 시간이 긴 비즈니스는 스레드 풀을 어떻게 사용합니까? 높은 동시성과 긴 비즈니스 실행 시간으로 비즈니스에 스레드 풀을 사용하는 방법은 무엇입니까?

이것은 동시 프로그래밍 웹 사이트에서 본 질문입니다.이 질문을 마지막 질문에 올렸습니다.이 질문은 매우 훌륭하고 실용적이며 매우 전문적이기 때문에 모든 사람이보고 생각할 수 있기를 바랍니다. 이 문제와 관련하여 제 개인적인 의견은 다음과 같습니다.

1) 동시성이 높고 작업 실행 시간이 짧은 서비스의 경우 스레드 풀의 스레드 수를 CPU 코어 수 + 1로 설정하여 스레드 컨텍스트 전환을 줄일 수 있습니다.

2) 낮은 동시성과 긴 작업 실행 시간을 가진 비즈니스는 구별되어야합니다.

a) 비즈니스 시간이 IO 작업, 즉 IO 집약적 인 작업에 집중되어있는 경우 IO 작업이 CPU를 차지하지 않으므로 모든 CPU가 유휴 상태가되지 않도록하면 스레드 풀의 스레드 수를 늘릴 수 있습니다. CPU가 더 많은 비즈니스를 처리하도록

b) 업무 시간이 길고 컴퓨팅 작업, 즉 계산 집약적 인 작업에 집중된 경우이를 수행 할 방법이 없습니다. (1)과 동일하게 스레드 풀의 스레드 수를 줄여 스레드를 줄입니다. 컨텍스트 전환

c) 높은 동시성 및 긴 비즈니스 실행 시간. 이러한 유형의 작업을 해결하는 열쇠는 스레드 풀이 아니라 전체 아키텍처 설계입니다. 이러한 비즈니스의 일부 데이터를 캐시 할 수 있는지 확인하는 것이 첫 번째 단계이고 서버를 추가하는 것이 두 번째 단계입니다. 스레드 풀의 설정은 스레드 풀에 대한 다른 기사를 참조하십시오. 마지막으로, 미들웨어를 사용하여 작업을 분할하고 분리 할 수 ​​있는지 확인하기 위해 긴 비즈니스 실행 시간 문제를 분석해야 할 수도 있습니다.

참조 링크

추천

출처blog.csdn.net/qq_40220309/article/details/104657998