Java Multithreading이란 무엇입니까?
Java는 여러 작업을 동시에 (동시에 독립적으로) 처리하는 메커니즘을 제공합니다. 여러 스레드가 동일한 JVM 프로세스에서 공존하므로 동일한 메모리 공간을 공유합니다. 여러 프로세스와 비교하여 여러 스레드 간의 통신이 더 가볍습니다. 내 이해에 따르면, Java Multithreading은 전적으로 CPU 활용도를 향상시키기위한 것입니다. Java 스레드에는 새로운, 달리기 가능, 차단 및 죽은 4 개의 상태가 있습니다. 키는 차단입니다. 차단은 대기를 의미합니다. 차단 스레드는 스레드 디스패처의 타임 슬라이스 할당에 참여하지 않으므로 당연히 CPU를 사용하지 않습니다. 멀티 스레드 환경에서는 차단되지 않은 스레드가 실행되어 CPU를 최대한 활용합니다.
40 개의 질문 요약
1. 멀티 스레딩의 사용은 무엇입니까?
많은 사람들에게 말도 안되는 것처럼 보일 수있는 질문 : 나는 단지 멀티 스레딩을 사용할 수 있지만 무엇을 사용합니까? 제 생각에는이 대답은 더욱 말도 안됩니다. 소위 "진실이 무엇인지 아는"것은 "진실이 무엇인지 아는", "진실이 무엇인지 아는", "왜"는 "진실이 무엇인지 아는 것"입니다. "진실이 무엇인지 아는 것"의 수준에 도달하면 지식 포인트를 자유롭게 적용 할 수 있다고 할 수 있습니다. 좋아,이 문제에 대해 어떻게 생각하는지 말해 드리겠습니다.
(1) 멀티 코어 CPU의 장점에 대한 완전한 플레이
업계의 발전으로 오늘날의 랩톱, 데스크톱 및 상업용 응용 프로그램 서버는 모두 듀얼 코어이며 4 코어, 8 코어 또는 16 코어는 드문 일이 아닙니다. 단일 스레드 프로그램 인 경우 듀얼 코어 CPU에서 50%가 낭비되고 4 코어 CPU에서 75%가 낭비됩니다. 단일 코어 CPU의 소위 "멀티 스레딩"은 가짜 멀티 스레딩입니다. 프로세서는 동시에 논리 조각 만 처리하지만 스레드는 비교적 빠르게 전환하여 여러 스레드가 "동시에"실행되는 것처럼 보입니다. 멀티 코어 CPU의 멀티 스레딩은 실제 멀티 스레딩입니다. 다중 세그먼트 논리가 동시에 작동하고 멀티 스레딩을 수행 할 수 있습니다. CPU를 최대한 활용하려는 목표를 달성하기 위해 멀티 코어 CPU의 장점을 진정으로 제공 할 수 있습니다.
(2) 막힘 방지
프로그램 운영 효율성의 관점에서, 단일 코어 CPU는 멀티 스레딩의 장점에 대한 완전한 플레이를 제공 할뿐만 아니라 단일 코어 CPU에서 멀티 스레딩을 실행하면 스레드 컨텍스트 스위칭을 유발하여 프로그램의 전반적인 효율성을 줄일 수 있기 때문에 프로그램의 전반적인 효율성을 줄입니다. 그러나 막힘을 방지하기 위해 단일 코어 CPU에 멀티 스레딩을 적용해야합니다. 예를 들어 특정 데이터를 원격으로 읽는 등 스레드가 차단되는 한 단일 코어 CPU가 단일 스레드를 사용하는 경우, 피어가 오랫동안 반환되지 않았으며 타임 아웃 시간을 설정하지 않았다면 데이터가 반환되기 전에 전체 프로그램이 실행 중지됩니다. 멀티 스레딩은이 문제를 방지 할 수 있습니다. 여러 스레드가 동시에 실행됩니다. 한 스레드의 코드가 데이터를 읽지 않아도 다른 작업의 실행에 영향을 미치지 않습니다.
(3) 모델이 쉽습니다
이것은 분명하지 않은 또 다른 장점입니다. 단일 스레드 프로그래밍의 큰 작업이 있다고 가정하면 많은 것을 고려해야하며 전체 프로그램 모델을 구축하는 것이 더 번거 롭습니다. 그러나이 큰 작업 A를 여러 개의 작은 작업, 작업 B, 작업 C 및 작업 D로 나누면 프로그램 모델을 별도로 구축하고 여러 스레드를 통해 이러한 작업을 개별적으로 실행하면 훨씬 간단합니다.
2. 스레드를 만드는 방법
더 일반적인 문제는 일반적으로 두 가지입니다.
(1) 스레드 클래스를 상속합니다
(2) 실행 가능한 인터페이스를 구현하십시오
어느 쪽이 더 나은지에 대해서는 인터페이스를 구현하는 방법이 상속 클래스 방법보다 유연하고 프로그램 간의 결합을 줄일 수 있기 때문에 후자는 확실히 더 좋습니다. 인터페이스 지향 프로그래밍은 또한 설계 패턴의 6 가지 주요 원칙의 핵심입니다.
3. start () 메소드와 run () 메소드의 차이
start () 메소드가 호출 될 때만 멀티 스레딩의 특성이 표시되고 다른 스레드의 run () 메소드의 코드가 교대로 실행됩니다. run () 메소드를 호출하면 코드가 동시에 실행됩니다. 다른 스레드가 run () 메소드에서 코드를 실행하기 전에 한 스레드의 run () 메소드의 코드가 기다려야합니다.
4. 실행 가능한 인터페이스와 호출 가능한 인터페이스의 차이
약간의 깊은 질문이 있으며, 또한 Java 프로그래머가 배운 지식의 폭을 보여줍니다.
runnable 인터페이스에서 run () 메소드의 반환 값은 무효이며, 그 일은 run () 메소드에서 코드를 실행하는 것입니다. Callable 인터페이스의 Call () 메소드는 일반적으로 리턴 값을 가지며, 이는 일반적으로 미래 및 FutureTask와 함께 비동기 실행 결과를 얻는 데 사용할 수 있습니다.
멀티 스레딩이 단일 스레딩보다 더 어렵고 복잡한 주요 이유는 멀티 스레딩이 알려지지 않은 것으로 가득 차 있기 때문입니다. 특정 스레드가 실행 되었습니까? 스레드가 얼마나 오래 실행 되었습니까? 스레드가 실행될 때 예상되는 데이터입니까? 우리가 할 수있는 일은이 멀티 스레드 작업이 실행되기를 기다리는 것입니다. Callable+Future/FutureTask는 다중 스레드 실행 결과를 얻을 수 있으며 대기 시간이 너무 길고 필요한 데이터를 얻지 못하면 스레드 작업을 취소 할 수 있습니다. 정말 유용합니다.
5. Cyclicbarrier와 CountdownLatch의 차이
약간 유사하게 보이는 두 클래스는 코드가 java.util.concurrent의 특정 지점에서 실행되는 것을 나타낼 수 있습니다. 이 둘의 차이점은 다음과 같습니다.
(1) Cyclicbarrier의 스레드가 특정 지점에서 실행 된 후 스레드가 실행을 중지합니다. 모든 스레드 가이 시점에 도달 할 때까지 모든 스레드는 다시 실행되지 않습니다. Countdownlatch가 아닙니다. 스레드가 특정 지점에서 실행되면 특정 값 -1 만 제공하고 스레드는 계속 실행됩니다.
(2) Cyclicbarrier는 하나의 작업 만 불러 일으킬 수 있으며 CountdownLatch는 여러 작업을 불러 일으킬 수 있습니다.
(3) Cyclicbarrier가 재사용 될 수 있고, CountdownLatch는 재사용 할 수 없으며, 카운트 값은 0이되며 CountdownLatch는 다시 사용되지 않습니다.
6. 휘발성 키워드의 역할
매우 중요한 문제는 멀티 스레딩을 배우고 적용하는 모든 Java 프로그래머가 마스터해야한다는 것입니다. 휘발성 키워드의 역할을 이해하기위한 전제 조건은 Java 메모리 모델을 이해하는 것입니다. 여기서는 Java 메모리 모델에 대해서는 이야기하지 않을 것입니다. 31 점을 참조 할 수 있습니다. 휘발성 키워드에는 두 가지 주요 기능이 있습니다.
(1) 멀티 스레딩은 주로 가시성과 원자력의 두 가지 특성을 중심으로 진행됩니다. 휘발성 키워드로 수정 된 변수는 여러 스레드 간의 가시성, 즉 휘발성 변수를 읽을 때마다 최신 데이터 여야합니다.
(2) 기본 코드 실행은 우리가 보는 고급 언어 인 Java 프로그램만큼 간단하지 않습니다. 그 실행은 Java 코드 -> bytecode -> 해당 C/C ++ 코드를 실행합니다. Bytecode -> c/c ++ 코드는 어셈블리 언어로 컴파일되어 하드웨어 회로와 상호 작용합니다. 실제로, 더 나은 성능을 얻기 위해 JVM은 지침을 재정렬 할 수 있으며, 다중 스레딩에서는 예기치 않은 문제가 발생할 수 있습니다. 휘발성을 사용하면 시맨틱 재정렬이 금지되며, 이는 코드 실행 효율성이 어느 정도까지 감소합니다.
실제적인 관점에서 볼 때, 휘발성의 중요한 역할은 CAS와 결합하여 원자력을 보장하는 것입니다. 자세한 내용은 atomicinteger와 같은 java.util.concurrent.atomic 패키지의 클래스를 참조하십시오.
7. 실 안전이란 무엇입니까?
또 다른 이론적 질문에는 많은 답이 있습니다. 개인적으로 가장 좋은 설명이라고 생각하는 것이 좋습니다. 멀티 스레딩 및 단일 스레딩에서 실행될 때 코드가 항상 동일한 결과를 얻을 수 있다면 코드는 스레드-안전입니다.
이 문제에 대해 언급 할 가치가 있습니다. 즉, 몇 가지 수준의 스레드 안전이 있습니다.
(1) 불변
문자열, 정수, 긴 등과 마찬가지로, 그들은 모두 최종 유형입니다. 스레드는 값을 변경할 수 없습니다. 당신이 그것들을 바꾸고 싶다면, 당신은 새로운 것을 만들지 않을 것입니다. 따라서 이러한 불변의 객체는 동기화 수단없이 다중 스레드 환경에서 직접 사용할 수 있습니다.
(2) 절대 스레드 안전성
런타임 환경에 관계없이 발신자는 추가 동기화 측정이 필요하지 않습니다. 이를 위해서는 보통 많은 추가 비용을 지불해야합니다. Java에서는 실제로 스레드 안전 클래스가 아닙니다. 그러나 CopyonWriteArrayList 및 CopyOnWriteArrayset과 같은 절대적으로 스레드-안전한 클래스도 있습니다.
(3) 상대 실 안전
상대 스레드 안전은 우리가 일반적으로 스레드 안전이라고 부르는 것입니다. 예를 들어, 벡터, 추가 및 제거 방법은 원자 연산이며 중단되지 않지만 이로 제한됩니다. 특정 벡터를 가로 지르는 스레드가 동시에 스레드가 추가되면 동시 변형 사례가 99%에서 발생하며, 이는 실패 메커니즘입니다.
(4) 스레드는 안전하지 않습니다
이것에 대해 할 말이 없습니다. Arraylist, Linkedlist, Hashmap 등은 모두 스레드가 아닌 클래스입니다.
8. Java에서 스레드 덤프 파일을 얻는 방법
Dead Loop, 교착 상태, 차단, 느린 페이지 오프닝 등과 같은 문제의 경우 스레드 덤프를 치는 것이 문제를 해결하는 가장 좋은 방법입니다. 소위 스레드 덤프는 스레드 스택입니다. 스레드 스택을 얻기위한 두 단계가 있습니다.
(1) 스레드의 PID를 받으면 JPS 명령을 사용할 수 있으며 PS -EF | Linux 환경에서 Grep Java
(2) 스레드 스택 인쇄, Jstack PID 명령을 사용할 수 있으며 Linux 환경에서 킬 -3 PID를 사용할 수도 있습니다.
또한 스레드 클래스는 스레드 스택을 가져 오는 데 사용할 수있는 getStackTrace () 메소드를 제공합니다. 이것은 인스턴스 메소드 이므로이 메소드는 특정 스레드 인스턴스에 바인딩됩니다. 매번 특정 스레드에서 스택을 실행할 때마다.
9. 스레드에 런타임 예외가 있으면 어떻게됩니까?
이 예외가 잡히지 않으면 스레드가 실행 중지됩니다. 또 다른 중요한 점은 :이 스레드가 특정 객체의 모니터를 보유하면 객체 모니터가 즉시 해제됩니다.
10. 두 스레드간에 데이터를 공유하는 방법
스레드간에 객체를 공유 한 다음 await/notify/notifyall, 기다려/신호/Signalall을 통해 발동하고 기다리십시오. 예를 들어, 블록 큐 블록 큐어는 스레드간에 데이터를 공유하도록 설계되었습니다.
11. 수면 방법과 대기 방법의 차이점은 무엇입니까?
이 질문은 종종 수면 방법과 대기 방법을 사용하여 일정 시간 동안 CPU를 포기하는 데 사용될 수 있습니다. 차이점은 스레드가 객체의 모니터를 보유하면 수면 방법 이이 객체의 모니터를 포기하지 않으며 대기 방법 이이 객체의 모니터를 포기한다는 것입니다.
12. 생산자 소비자 모델의 역할은 무엇입니까?
이 질문은 이론적이지만 중요합니다.
(1) 생산자 소비자 모델의 가장 중요한 역할 인 생산자 및 소비자 소비 용량의 생산 능력의 균형을 유지함으로써 전체 시스템의 운영 효율성을 향상시킵니다.
(2) 분리, 이것은 생산자와 소비자 모델과 함께 기능입니다. 분리는 생산자와 소비자 사이에 연결이 적다는 것을 의미합니다. 연결이 적을수록 상호 제약을받지 않고 스스로 개발할 수 있습니다.
13. ThreadLocal의 사용은 무엇입니까?
간단히 말해서 Threadlocal은 시간 동안 공간을 교환하는 관행입니다. 각 스레드는 개방형 주소 메소드에서 구현하여 데이터를 분리하고 데이터를 공유하지 않으므로 자연스럽게 스레드 안전 문제가 없을 것입니다.
14. WAKE () 및 notify ()/notifyall () 메서드가 동기화 블록에서 호출되는 이유
이것은 JDK에 의해 강요됩니다. 대기 () 메소드 및 notify ()/notifyall () 메소드는 먼저 호출하기 전에 개체의 잠금을 얻어야합니다.
15. wait () 메소드와 notify ()/notifyall () 메소드의 차이점은 객체 모니터를 포기할 때 무엇입니까?
객체 모니터를 포기할 때 대기 () 메소드와 notify ()/notifyall () 메소드의 차이는 대기 () 메소드가 객체 모니터를 즉시 공개하는 반면, 객체 모니터를 포기하기 전에 나머지 코드가 실행될 때까지 대기한다는 것입니다.
16. 스레드 풀을 사용하는 이유
스레드 객체의 재사용을 달성하기 위해 자주 생성과 스레드의 파괴를 피하십시오. 또한 스레드 풀을 사용하면 프로젝트에 따라 동시성 수를 유연하게 제어 할 수 있습니다.
17. 스레드가 객체 모니터를 보유하는지 여부를 감지하는 방법
또한 인터넷에서 멀티 스레드 인터뷰 질문을 보았습니다. 스레드가 객체 모니터를 보유하는지 여부를 결정하는 방법이 있음을 알 수 있습니다. 스레드 클래스는 HoldSlock (Object OBJ) 메소드를 제공합니다. 이것은 정적 메소드이며, 이는 "특정 스레드"가 현재 스레드를 의미한다는 것을 의미합니다.
18. 동기화와 재진입 락의 차이
Synchronized는 동일한 키워드와 같은 키워드이며, 그렇지 않으며, while 및 ReintrantLock은 클래스이며, 이는 둘 사이의 필수 차이입니다. ReintrantLock은 클래스이므로 상속 받고 방법이 있으며 다양한 클래스 변수가있는 동기화 된 것보다 점점 더 유연한 기능을 제공합니다. 동기화 된 것보다 재진입 락의 확장 성은 여러 지점에 반영됩니다.
(1) 재 렌트 링크 락은 자물쇠를 획득하기위한 대기 시간을 설정하여 교착 상태를 피할 수 있습니다.
(2) ReintrantLock은 다양한 잠금 정보를 얻을 수 있습니다
(3) ReintrantLock은 다 채널 알림을 유연하게 구현할 수 있습니다
또한,이 둘의 잠금 메커니즘은 실제로 다릅니다. 기본 재진입 락은 안전하지 않은 공원 방법을 잠그기 위해 호출하고 동기화 된 작업은 객체 헤더의 마크 워드 여야합니다.
19. Concurrenthashmap의 동시성은 무엇입니까?
ConcurrEthashMap의 동시성은 기본적으로 16 인 세그먼트의 크기입니다. 즉, 최대 16 개의 스레드가 동시에 ConcurRenthashMap을 작동 할 수 있습니다. 이것은 또한 Hashtable에서 ConsherThashmap의 가장 큰 장점입니다. 어쨌든, hashtable은 동시에 두 개의 스레드를 가질 수 있습니까?
20. readwritelock이란 무엇입니까?
우선, 재진입 락이 좋지 않다는 것이 아니라는 것을 분명히하자. ReintrantLock을 사용하는 경우 스레드 A 스레드로 인한 데이터 불일치를 방지하고 데이터 스레드 B 읽기 데이터를 방지 할 수 있습니다. 그러나 이러한 방식으로 스레드 C 읽기 데이터와 스레드 D도 데이터를 읽는 경우 데이터를 읽으면 데이터가 변경되지 않습니다. 잠글 필요는 없지만 여전히 잠그면 프로그램의 성능이 줄어 듭니다.
이 때문에 읽기 쓰기 잠금 readwritelock이 탄생했습니다. readWritelock은 read-write 잠금 인터페이스입니다. ReentrantreadWritelock은 Readwritelock 인터페이스의 구체적인 구현으로 읽기 및 쓰기의 분리를 실현합니다. 읽기 잠금 장치는 공유되며 쓰기 잠금 장치는 독점적입니다. 읽고 읽고 읽는 것은 상호 배타적이지 않습니다. 읽기 및 쓰기, 쓰기, 읽기, 쓰기 및 쓰기 만 상호 배타적이며 읽기 및 쓰기의 성능을 향상시킵니다.
21. Futuretask 란 무엇입니까?
이것은 실제로 앞에서 언급되었습니다. FutureTask는 비동기 작동 작업을 나타냅니다. Callable의 특정 구현 클래스를 FutureTask로 전달할 수 있으며,이 비동기 작업의 결과를 기다릴 수 있으며, 완료되었는지 확인하고 작업을 취소 할 수 있습니다. 물론 FutureTask는 런닝 가능한 인터페이스의 구현 클래스이기 때문에 FutureTask도 스레드 풀에 배치 할 수 있습니다.
22. Linux 환경에서 가장 긴 CPU를 사용하는 스레드를 찾는 방법
이것은 더 실용적인 문제이며,이 문제는 매우 의미가 있다고 생각합니다. 당신은 이것을 할 수 있습니다 :
(1) 프로젝트의 PID, JPS 또는 PS -EF를 받으십시오 | 이전에 언급 된 Grep Java
(2) 상단 -H -P PID, 주문을 변경할 수 없습니다.
이것은 현재 프로젝트가 각 스레드에 대해 취하는 CPU 시간의 백분율을 인쇄합니다. 여기에 하나는 LWP이며, 이는 운영 체제 기본 스레드의 스레드 번호입니다. 내 노트북 Mountain은 Linux 환경에 Java 프로젝트를 배치하지 않으므로 스크린 샷 및 시연을 수행 할 방법이 없습니다. 회사가 Linux 환경을 사용하여 프로젝트를 배포하는 경우 시도해 볼 수 있습니다.
"TOP -H -P PID" + "JPS PID"를 사용하면 높은 CPU를 차지하는 스레드 스택을 쉽게 찾을 수 있으므로 CPU가 높은 점유 원인을 배치 할 수 있습니다.
마지막으로, "Top -H -P PID"로 연주 한 LWP는 소수점이며 "JPS PID"와 함께 연주 한 로컬 스레드 번호는 16 진수입니다. 변환 후 높은 CPU를 차지하는 현재 스레드 스택을 찾을 수 있습니다.
23. Java 프로그래밍 교착 상태를 일으킬 프로그램 작성
나는 처음 으로이 질문을 보았고 그것이 아주 좋은 질문이라고 생각했습니다. 많은 사람들은 교착 상태가 무엇인지 알고 있습니다. 스레드 A와 스레드 B는 서로의 자물쇠가 무한 데드 루프가 프로그램을 계속하도록 기다리고 있습니다. 물론, 그것은 이것에만 국한됩니다. 교착 상태 프로그램을 작성하는 방법을 묻는다면 알 수 없습니다. 무뚝뚝하게 말하면, 당신은 교착 상태가 무엇인지 이해하지 못합니다. 이론을 이해하면 끝날 것입니다. 기본적으로 교착 상태 문제를 실제로 볼 수 없습니다.
교착 상태가 무엇인지 진정으로 이해하려면이 질문은 어렵지 않습니다. 몇 가지 단계가 있습니다.
(1) 두 개의 스레드는 각각 Lock1과 Lock2의 객체를 각각 보유합니다. 이 두 자물쇠는 동기 코드 블록의 잠금 장치 역할을합니다.
(2) run () 스레드 1의 메소드에서 동기화 코드 블록은 먼저 Lock1, thread.sleep (xxx)의 객체 잠금을 얻습니다. 시간은 너무 많이 걸리지 않으며 50 밀리 초가 거의 동일하며 Lock2의 객체 잠금을 얻습니다. 이것은 주로 스레드 1이 Lock1과 Lock2의 두 객체의 객체 잠금을 지속적으로 얻지 못하도록하기 위해 수행됩니다.
(3) 실행 2) (메소드에서 동기화 코드 블록은 먼저 객체 lock2를 얻은 다음 객체 잠금 1을 얻습니다.
이런 식으로, 스레드 1 "잠자기"후 스레드 2는 객체 Lock2를 획득했습니다. 스레드 1은 현재 객체 Lock2를 획득하려고 시도하고 차단됩니다. 현재 교착 상태가 형성됩니다. 더 이상 코드를 쓰지 않고 많은 공간이 필요합니다. Java Multithreading 7 : 교착 상태이 기사에는 위의 단계의 코드 구현이 포함되어 있습니다.
24. 차단 스레드를 깨우는 방법
wait (), sleep () 또는 join () 메소드로 인해 스레드가 차단되면 스레드를 방해하고 인터럽트 한 예를 던져서 깨어날 수 있습니다. 스레드가 IO 차단을 만나면 운영 체제에서 IO를 구현하고 Java 코드가 운영 체제에 직접 연락 할 수 없기 때문에 무력이 없습니다.
25. 불변의 물체가 멀티 스레딩에 도움이되는 어떤 도움이됩니까?
위에서 언급 한 문제는 불변의 객체가 객체의 메모리 가시성을 보장하고 불변의 객체를 읽기 위해 추가 동기화가 필요하지 않아 코드 실행 효율성을 향상시킵니다.
26. 다중 스레드 컨텍스트 전환이란 무엇입니까?
멀티 스레드 컨텍스트 전환은 CPU 제어를 한 실행 스레드에서 다른 스레드로 준비하고 CPU 실행 권한을 얻기를 기다리는 프로세스를 말합니다.
27. 작업을 제출할 때 스레드 풀 큐가 가득 차면이 시점에서 어떻게 될까요?
LinkedBlockingqueue, 즉 무한한 대기열을 사용하는 경우 중요하지 않습니다. LinkedBlockingqueue는 거의 무한 대기열로 간주 될 수 있고 작업을 무한대로 저장할 수 있기 때문에 블록 큐에 작업을 계속 추가하고 실행을 기다립니다. arrayblockingqueue와 같은 경계 큐를 사용하는 경우 먼저 ArrayBlockingQueue에 작업이 추가됩니다. ArrayBlockingQueue가 가득 차면 거부 executionHandler는 거부 정책을 사용하여 전체 작업을 처리하고 기본값은 낙관적입니다.
28. Java에서 사용되는 스레드 스케줄링 알고리즘은 무엇입니까?
선제 스타일. 스레드가 CPU를 사용한 후 운영 체제는 스레드 우선 순위, 스레드 굶주림 등과 같은 데이터를 기반으로 총 우선 순위를 계산하고 다음에 실행을 위해 스레드에 슬라이스를 할당합니다.
29. Shood.sleep (0)의 기능은 무엇입니까?
이 질문은 위의 질문과 관련이 있으며 저는 모두 함께합니다. Java는 선제 적 스레드 스케줄링 알고리즘을 사용하기 때문에 스레드가 종종 CPU 제어를 얻는 것이 발생할 수 있습니다. CPU 컨트롤을 얻기 위해 우선 순위가 상대적으로 낮은 스레드를 허용하기 위해 Thread.Sleep (0)는 운영 체제를 수동으로 트리거하는 데 사용될 수 있으며, 이는 CPU 제어의 균형을 맞추는 작업이기도합니다.
30. 스핀이란 무엇입니까?
많은 동기화 된 코드는 매우 간단한 코드이며 실행 시간은 매우 빠릅니다. 스레드 차단에는 사용자 상태 및 커널 상태 전환이 포함되기 때문에이 시간에 대기 대기 스레드를 잠그는 것이 가치가없는 작업 일 수 있습니다. 동기화 된 코드는 매우 빠르게 실행되므로 잠금을 기다리는 스레드가 차단되지 않도록 할 수도 있지만 대신 동기화 된 경계에서 바쁜 루프를 수행 할 수 있습니다. 이것은 스핀입니다. 여러 번 바쁜 루프를 수행하고 잠금 장치가 얻어지지 않았다는 것을 알게 된 다음 차단하면 더 나은 전략 일 수 있습니다.
31. Java 메모리 모델이란 무엇입니까?
Java 메모리 모델은 Java 메모리에 대한 멀티 스레딩 액세스를위한 사양을 정의합니다. Java 메모리 모델을 완전히 설명해야하지만 여기서 몇 문장으로 명확하게 설명 할 수는 없습니다. 간단히 요약하겠습니다.
Java 메모리 모델의 여러 부분 :
(1) Java 메모리 모델은 메모리를 기본 메모리 및 작업 메모리로 나눕니다. 클래스의 상태, 즉 클래스간에 공유되는 변수는 메인 메모리에 저장됩니다. Java 스레드가 주 메모리에서 이러한 변수를 사용할 때마다 기본 메모리의 변수를 읽고 자체 작업 메모리에 존재하게합니다. 자체 스레드 코드를 실행할 때는 이러한 변수를 사용하고 자체 작업 메모리에서 작동합니다. 스레드 코드가 실행되면 최신 값이 기본 메모리로 업데이트됩니다.
(2) 여러 원자 연산이 주 메모리 및 작업 메모리에서 변수를 작동하도록 정의됩니다.
(3) 휘발성 변수를 사용하기위한 규칙을 정의합니다
(4), 즉, 즉, 첫 번째 발생 원칙은 작동 B에서 먼저 작동 해야하는 규칙을 정의합니다. 예를 들어, 동일한 스레드의 제어 흐름 앞의 코드는 제어 흐름 뒤에있는 코드에서 먼저 발생해야합니다. 잠금 잠금 해제 조치는 동일한 잠금 장치 잠금 작업에서 먼저 발생해야합니다. 특정 코드가 모든시기에 모든 규칙을 준수하지 않으면이 코드는 스레드가 아닌 안전이어야합니다.
32. CAS 란 무엇입니까?
전체 이름 비교 및 세트 인 CAS는 비교입니다. 메모리 값 v, 기존 예상 값 a, 수정할 값 B의 세 가지 피연산자가 있다고 가정합니다. 예상 값 A와 메모리 값 V가 동일한 경우에만 메모리 값이 B로 수정되고 True가 반환됩니다. 그렇지 않으면 아무것도 수행되지 않고 False가 반환됩니다. 물론, CAS는 휘발성 변수와 협력하여 각 시간마다 얻은 변수가 기본 메모리의 최신 값인지 확인해야합니다. 그렇지 않으면 이전 예상 값 A는 항상 스레드에 대해 변경되지 않는 값 A입니다. 특정 CAS 운영이 실패하는 한 성공하지 못할 것입니다.
33. 낙관적 잠금과 비관적 잠금 장치는 무엇입니까?
(1) 낙관적 잠금 : 이름과 마찬가지로 동시 작업으로 인한 스레드 안전 문제에 대해 낙관적입니다. 낙관적 잠금 장치는 경쟁이 항상 발생하지는 않다고 생각하므로 잠금 장치를 유지할 필요는 없으며 비교할 필요가 없으며,이 두 가지 동작을 메모리의 변수를 수정하기 위해 원자 연산으로 설정합니다. 실패하면 충돌이 발생한다는 것을 의미하며 해당 레트리 논리가 있어야합니다.
(2) 비관적 잠금 : 이름과 마찬가지로 동시 작업으로 인한 스레드 안전 문제에 대해 비관적입니다. 비관적 인 자물쇠는 경쟁이 항상 일어날 것이라고 믿는다. 따라서 리소스가 작동 할 때마다 직접 잠긴지 여부에 관계없이 자원이 동기화 된 것처럼 독점 잠금 장치를 보유하고 리소스가 작동됩니다.
34. AQS 란 무엇입니까?
AQ에 대해 간단히 이야기 해 봅시다. AQS의 전체 이름은 추상적 인 정신병자입니다. 번역 될 때 추상 대기열 동기화제 여야합니다.
java.util.concurrent의 기초가 CAS 인 경우 AQS는 전체 Java 동시성 패키지의 핵심이며, ReintrantLock, CountdownLatch, Semaphore 등이 모두 사용됩니다. AQS는 실제로 모든 항목을 양방향 대기열 형태로 연결합니다. 예를 들어, ReintrantLock. 모든 대기 스레드는 항목에 배치되어 양방향 대기열에 연결됩니다. 이전 스레드가 ReintrantLock을 사용하는 경우 양방향 대기열의 첫 번째 항목이 실제로 실행되기 시작합니다.
AQS는 양방향 대기열의 모든 작업을 정의하지만 개발자가 사용할 수있는 TryLock 및 TryRelease 방법 만 엽니 다. 개발자는 자체 구현에 따라 TryLock 및 TryRelease 방법을 다시 작성하여 자체 동시 기능을 구현할 수 있습니다.
35. 싱글 톤 모드의 스레드 안전성
진부한 문제는, 가장 먼저 말해야 할 것은 싱글 톤 패턴의 실 안전은 다음을 의미한다는 것을 의미합니다. 특정 클래스의 인스턴스는 다중 스레드 환경에서 한 번만 만들어 질 것입니다. 싱글 톤 패턴을 작성하는 방법에는 여러 가지가 있습니다. 요약하겠습니다.
(1) 배고픈 사람의 싱글 톤 패턴 작성 : 실 안전
(2) 게으른 싱글 톤 패턴 작성 : 비 스레드-안전
(3) 이중 점검 잠금의 싱글 톤 모드 작성 : 스레드 안전
36. 세마포어의 기능은 무엇입니까?
세마포어는 세마포어이며, 그 기능은 특정 코드 블록의 동시성 수를 제한하는 것입니다. Semaphore에는 int Integer n을 통과 할 수있는 생성자가 있으며 N 스레드 만 특정 코드에 액세스 할 수 있음을 나타냅니다. n을 초과하면 스레드가 코드 블록을 완료하고 다음 스레드를 입력 할 때까지 기다리십시오. 이것으로부터 우리는 세마포어 생성자에 통과 된 int 정수 n = 1이 동기화되는 것과 동일하다는 것을 알 수 있습니다.
37. Hashtable의 Size () 메소드에는 "반환 수"가 하나만 있으므로 왜 여전히 동기화해야합니까?
이것은 내가 전에 가지고 있었던 혼란이며, 당신 이이 질문에 대해 생각했는지 궁금합니다. 메소드에 여러 문장이 있고 모두 동일한 클래스 변수를 작동하는 경우 다중 스레드 환경에 잠금 장치를 추가하지 않으면 스레드 안전 문제가 발생합니다. 이것은 이해하기 쉽지만 Size () 메소드에는 명확하게 하나의 진술이 있으므로 왜 여전히 잠금 장치를 추가해야합니까?
이 문제와 관련하여, 나는 천천히 일하고 공부함으로써 그것을 이해했으며, 두 가지 주요 이유가 있습니다.
(1) 하나의 스레드만이 고정 클래스의 동기화 방법을 동시에 실행할 수 있지만, 클래스의 비동기 메소드의 경우 여러 스레드가 동시에 액세스 할 수 있습니다. 따라서 문제가 있습니다. 스레드 A는 해시 테이블의 PUT 메소드를 실행할 때 데이터를 추가 할 수 있으며 스레드 B는 Size () 메소드를 일반적으로 호출하여 해시 가능의 현재 요소 수를 읽을 수 있습니다. 읽기 값이 최신이 아닐 수도 있습니다. 스레드 A는 데이터를 추가했지만 크기 ++가 없으면 스레드 B는 이미 크기를 읽었습니다. 따라서 스레드 B의 경우 읽기 크기가 정확하지 않아야합니다. Size () 메소드에 동기화를 추가 한 후 스레드 B가 Size () 메소드를 호출 한 후 A PUT 메소드를 호출하여 스레드 안전을 보장합니다.
(2) CPU는 코드를 실행하지만 Java 코드는 아닙니다. 이것은 매우 중요하며 기억해야합니다. Java 코드는 결국 실행을위한 어셈블리 코드로 변환되며 어셈블리 코드는 하드웨어 회로와 진정으로 상호 작용할 수있는 코드입니다. Java 코드의 한 줄만 있다는 것을 알 수 있지만 Java 코드가 컴파일 된 것을 보더라도 바이트 코드의 한 줄만 생성 되더라도 기본 레이어에 대해서는이 명령문에 대해 하나의 작동만을 의미하지는 않습니다. 문장 "return count"는 실행할 3 개의 어셈블리 명령문으로 번역되었다고 가정하며, 첫 번째 문장이 실행 된 후 스레드가 전환 될 가능성이 전적으로 가능합니다.
38. 스레드는 스레드 클래스의 생성자이고 스레드가 호출 된 정적 블록입니다.
이것은 매우 까다 롭고 교활한 질문입니다. 기억하십시오 : 스레드 클래스의 생성자와 정적 블록은 새 스레드 클래스가있는 스레드에 의해 호출되고 실행 메소드의 코드는 스레드 자체에 의해 호출됩니다.
위의 진술이 당신을 혼란스럽게한다면, 예를 들어, 새 스레드 11이 Thread2에 있고 새로운 Thread2가 주요 함수에 있다고 가정 해 봅시다.
(1) Thread2의 생성자 및 정적 블록은 주 스레드에 의해 호출되며 Thread2의 run () 메소드는 Thread2 자체에 의해 호출됩니다.
(2) Thread1의 생성자 및 정적 블록은 Thread2에 의해 호출되며 Thread1의 run () 메소드는 Thread1 자체에 의해 호출됩니다.
39. 동기화 방법과 동기화 블록 사이의 더 나은 선택은 무엇입니까?
블록 동기화는 동기화 블록 외부의 코드가 비동기식으로 실행되므로 전체 메소드를 동기화하는 것보다 코드의 효율성을 향상시킵니다. 하나의 원칙을 알고 있습니다 : 동기화 범위가 적습니다
더 좋습니다.
이 기사를 사용하면 동기화 범위가 작을수록 더 좋을수록 Java Virtual Machines에서 Lock Coarsense라는 최적화 방법이 더 좋다고 언급하고 싶습니다. 이것은 유용합니다. 예를 들어 StringBuffer는 스레드 안전 클래스입니다. 당연히 가장 일반적으로 사용되는 Append () 메소드는 동기화 방법입니다. 코드를 작성하면 문자열을 반복적으로 추가 할 것입니다. 이는 반복적으로 잠금 -> 잠금 해제를 의미합니다. 이는 성능에 적합하지 않습니다. 이는 Java 가상 머신 이이 스레드의 커널 상태와 사용자 상태를 반복적으로 전환해야한다는 것을 의미합니다. 따라서 Java Virtual Machine은 다중 Append Method가 호출 한 코드에서 잠금 코어 작업을 수행하여 여러 부속 작업을 Append 메소드의 헤드와 테일로 확장하여 큰 동기화 블록으로 전환합니다. 이로 인해 잠금 잠금 장치 수가 줄어들어 코드 실행의 효율성이 효과적으로 향상됩니다.
40. 동시성이 높은 비즈니스에 스레드 풀을 사용하는 방법 및 짧은 작업 실행 시간은 무엇입니까? 동시성이 낮고 작업 실행 시간이 긴 비즈니스에 스레드 풀을 사용하는 방법은 무엇입니까? 동시성이 높고 서비스 실행 시간이 긴 비즈니스에 스레드 풀을 사용하는 방법은 무엇입니까?
이것은 내가 동시 프로그래밍 웹 사이트에서 본 질문입니다. 나는이 질문을 마지막으로하고 모든 사람들이 그것을보고 생각할 수 있기를 바랍니다.이 질문은 매우 좋고 실용적이며 매우 전문적이기 때문입니다. 이 문제와 관련하여 내 개인적인 의견은 다음과 같습니다.
(1) 높은 동시성 및 짧은 작업 실행 시간, 스레드 풀 스레드 수를 CPU 코어 번호 +1로 설정하여 스레드 컨텍스트의 전환을 줄일 수 있습니다.
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换
(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
Java线程阻塞(Blocked)的类型:
调用sleep函数进入睡眠状态,Thread.sleep(1000)或者TimeUnit.SECONDS.sleep(1),sleep不会释放锁。
等待(wait)某个事件,分为两种,(wait,notify,notifyAll),(await, signal,signalAll) ,后面会详细介绍。wait和await会释放锁,且必须在获取到锁的环境才能调用。
等待锁,synchronized和lock环境中,锁已经被别的线程拿走,等待获取锁。
IO阻塞(Blocked),比如网络等待,文件打开,控制台读取。System.in.read()。