요약 및 유도 "의 분야에서 자바 동시성"을 읽은 후. 당신은 질문이있는 경우에, 저를 수정하시기 바랍니다.
제 1 장 동시 프로그래밍 과제
1.1 문맥 전환
작업은 다음 시간에 작업로 전환하기 위해 작업의 상태를 전환하기 전에 저장됩니다 1. 당신은 그 작업의 상태를로드 할 수 있습니다. 그래서 작업은 다시로드하는 과정에서 컨텍스트 스위치를 저장하는 것입니다.
2. 어떻게 컨텍스트 스위칭을 줄이기 위해?
-
잠금이없는 동시 프로그래밍
-
CAS 알고리즘
-
최소 스레드
-
사용하여 코 루틴 : 멀티 태스크 스케줄링은 단일 스레드로 구현하고, 단일 스레드에 복수의 태스크 사이의 스위칭을 유지
1.2 교착 상태
1. 어떻게 교착 상태를 피하기 위해?
-
스레드가 더 잠금을 얻을하지 마십시오
-
더 많은 자원을 차지 다시 스레드 잠금을 피하고, 오직 하나의 스레드가 자원을 차지할 수 있도록 노력
-
시간 잠금을 사용 lock.tryLock (타임 아웃)를 사용하는 대신 내부 로킹 메커니즘을 사용하여 시도
-
데이터베이스 잠금의 경우, 데이터베이스 연결을 잠금 및 잠금 해제를해야한다, 그렇지 않으면 상황은 실패 나타납니다
구현 원리를 기초 제 2 장 자바 동시성 메커니즘
2.1 휘발성 응용 프로그램
-
휘발성 경량 그는 여러 동시 프로세서의 변수를 공유하기 위해, 동기화 "가시성" . 필드가 volatile로 선언 된 경우, Java 스레드 메모리 모델 보장하지만이 모든 스레드는이 변수의 값이 동일 볼 수있다.
-
이 컨텍스트 스위칭 및 스케줄링 발생하지 않습니다.
-
두 가지의 원칙의 휘발성 실현
- 프로세서 캐시 원인 잠금 접두사 지침 메모리에 다시 기록
- 프로세서의 캐시 메모리가 원인으로 다시 작성 다른 프로세서의 캐시가 잘못되었습니다
2.2 동기화
1.synchronized 기준은 동기화를 달성하기 위하여 자바 각 객체가 로크로서 사용될 수있다.
2. 세 가지 형태 :
- 통상 동기 방법, 로크는 오브젝트의 현재 인스턴스이고;
- 정적 동기화 방법, 로크는 현재 클래스의 클래스 오브젝트이고;
- 동기 부호 블록에 대한 개체 잠금 동기화 브래킷 구성된다.
비교 2.2.2 업그레이드 및 잠금
-
없음 잠금 상태 없다 - "편견 잠금 -"경량 잠금 - "헤비급 잠금. 잠금은 다운 그레이드 할 수 없습니다 업그레이드 할 수 있습니다.
-
닫기 지연을 고정 바이어스 (보통 바이어스 잠금은 프로그램이 몇 초 시작 후에 만 활성화됩니다)
-XX:BiasedLockingStartupDelay = 0;
- 닫기 바이어스 잠금 (경량 잠금 프로그램을 입력 한 후 기본 상태)
-XX:UseBiasedLocking = false;
- 경량 잠금 해제 잠금 헤비급 잠금 확장의 뒷면에있는 잠금 경합, 잠금의 현재 존재를 나타내는 실패합니다.
5. 잠금 장점과 단점 :
- 이점
-
바이어스 잠금 : 잠금 및 추가 소비를 해제하지 만이 차이를 나노초과 비교하여 비동기 방법을 수행한다
-
경량 잠금 : 스레드가 절차의 응답 속도를 향상, 경쟁을 차단하지 않습니다;
-
헤비급 잠금 : 스핀를 사용하지 않고 스레드 경쟁, CPU를 소비하지 않습니다
- 결점
-
잠금을 바이어스 : 스레드 잠금 경합이있는 경우, 소비의 추가 잠금 해지를 가져올 것이다;
-
경량 잠금 : 스레드를 경쟁 잠금을 가져올 수 없습니다 아직도 경우, 스핀을 사용하여 CPU를 소모합니다
-
헤비급 잠금 : 스레드가 차단되고, 느린 응답 시간
- 적용 현장
-
편향된 로킹 : 하나의 스레드 만이 동기 블록에 액세스하도록 구성되는
-
경량 잠금 : 응답 시간의 추구, 매우 빠르게의 구현 동기화 된 블록
-
헤비급 잠금 : 특정 동기 블록 긴 실행 속도의 추구
2.3 원자 조작
- 어떻게 프로세서 구현 원자 조작?
버스 키 보장 자성을 이용하여 (1);
캐시 잠금 보증 자성을 사용하여 (2);
이가지 경우를 제외하고 :
- 동작 데이터는 상기 프로세서 또는 프로세서 버스 로크 전화에서 데이터의 캐시 라인의 다수의 동작 내에 캐시 될 수 없을 때
- 일부 프로세서 캐시 잠금을 지원하지 않습니다
- 어떻게 자바 원자 연산을 구현하는 방법?
저자 로킹 및 환상 CAS 구현;
(1) CAS 사이클 동작의 자성을 이용하여
프로세서 구현에 의해 제공 CMPXCHG 명령하여 CAS JVM의 동작. 기본적인 아이디어는 스핀 사이클 CAS CAS 지금까지 성공적인 작동을 알 달성하는 것입니다.
세 원자 (2) CAS 작동
-
ABA 문제
- 해결 방법 : 버전 번호를 사용하여이 버전 번호가 변수 앞에 추가되고 각 변수가 업데이트 될 때 버전 번호 +1
-
대형 오버 헤드 긴 사이클 시간
-
만 공유 변수를 보장 할 수 원자 조작
(3) 달성하기위한 로크기구를 사용
이 메커니즘은 메모리 영역은 잠금 장치를 작동 할 수 있도록 잠금을 획득 할 수있는 스레드가 아니라 보장합니다.
제 4 장 자바 병행 프로그래밍의 기초
4.1 소개 스레드
4.1.1 스레드 무엇입니까
1. 현대 운영 체제 스케줄링의 최소 단위는 스레드라고도 경량 프로세스입니다 . 여러 스레드가이 스레드가 자신의 카운터, 스택, 스택 특성, 지역 변수를 가지고 프로세스를 생성 할 수 있으며, 공유 메모리 변수에 액세스 할 수 있습니다.
4.1.2를 사용하는 이유 멀티 스레딩
- 이상의 프로세서 코어
- 빠른 응답 시간
- 더 나은 프로그래밍 모델
4.1.3 스레드 우선 순위
-
자주 폐색 (수면 또는 I / O 동작)에 대한 스레드를 높은 우선 순위를 설정해야하고, 강조 계산 스레드는 프로세서가 독점되지 않도록 우선 순위를 낮추기 위해 설정된다 (CPU보다 연산 시간 또는 일부가 필요함);
-
OS가 우선 순위 설정을위한 자바 스레드를 무시할 수 있기 때문에 스레드 우선 순위는 프로그램의 정확성에 같은 종속되지 않습니다.
4.1.4 스레드 상태
4.1.5 데몬 스레드
-
주로 백그라운드 프로그램 예약 및 지원 작품으로 사용되는지지 형 스레드, 평균, 데몬 스레드가 자바 가상 머신을 존재하지 않는, Java 가상 머신이 종료됩니다 .
-
(참) Thread.setDarmon이 필요 스레드를 시작하기 전에 설정해야합니다, 설정할 수 있습니다.
-
데몬 스레드를 만들 때, 당신은 마지막으로 정리 또는 종료 논리 자원의 이행을 보장하기 위해 블록의 내용에 의존 할 수 없다.
4.2 시작 및 종료 스레드
4.2.1 건설 스레드
4.2.2 시작 스레드
초기화가 완료되면, 스레드를 시작할 수 있습니다 시작 () 메서드를 호출합니다. 스레드를 시작하기 전에 스레드는 스레드 이름을 설정하는 것이 가장 좋습니다.
4.2.3 중단 이해
-
인터럽트 : 다른 스레드에 의해 실행되는 스레드 인터럽트 조작되었는지 여부를 나타내는 플래그 스레드의 속성으로 이해했다. 다른 스레드 인터럽트와 마찬가지로 스레드가 인터럽트 작업 () 메소드를 인터럽트 호출하여 호출 스레드의 다른 스레드를했다.
-
중단 여부를 결정하는 스레드 (), 당신은 또한 isInterrupted에 의해 현재의 thread의 인터럽트 플래그 재설정 정적 방법 Thread.interrupted ()를 호출 할 수 있습니다.
4.3 스레드 간 통신
4.3.2 대기 / 알림 메커니즘
통화 대기 (),),) (주가는 notifyAll을 (통지 :
- 우리는 객체 잠금 전화를 걸 때 사용합니다;
- ()를 호출 대기 한 후, 대기 중, 현재 스레드 대상물 큐에 넣고, 실행 스레드 상태 변화;
- 대기에서 스레드로 복귀 할 수있는 기회를 기다린 후 통보하고가는 notifyAll하는, 호출 스레드 출시 잠금 요구를 통지의 notifyAll 호출 후, 아직도 대기 스레드는 대기에서 반환하지 않습니다;
- 통지의 notifyAll 동기 큐 스레드 상태 변화에 의해 이동이 차단 길 기다리고 큐로 이동 스레드를 대기
- 전제 대기 메소드가 리턴에서 호출 개체가 잠금을 확보하는 것입니다
4.3.3 대기 고전적인 패러다임 / 통지
파티 기다립니다 :
- 객체 잠금을 획득
- 조건이 충족되지 않는 경우, 통지 아직 확인 조건 인 후 객체의 대기 () 메소드를 호출
- 논리 상태를 대응하는 실행 만족
- 의사 코드
synchronized(对象) {
while(条件不满足) {
对象.wait();
}
// 对应的逻辑处理
}
파티 통보 :
- 객체 잠금을 얻으려면
- 변경 조건
- 대상 스레드에 대한 모든 대기 통보
- 의사 코드
synchronized(对象) {
改变条件
对象.notifyAll();
}
4.3.4 입력 및 출력 흐름 덕트
4.3.5를 Thread.join () 사용
스레드 A가 () 문을를 Thread.join를 실행하는 경우, 그 의미는 다음과 같습니다 스레드 대기 스레드 후 현재 스레드를 Thread.join ()에서 반환을 종료 할 수 있습니다. 스레드가 스레드를 제공 할뿐만 아니라 가입 () 메소드는 또한 (긴 밀)를 결합하고 (긴 분쇄기, 나노 INT) 두 개의 제한 수단을 포함하는 가입 제공된다.
4.4 스레드 응용 프로그램 예제
4.4.3 스레드 풀과 그 예
스레드 풀 : 이전 스레드, 스레드 창조의 여러 수를 생성하고 사용자가 직접 제어 할 수없는 임무를 완료하려면이 상황에서 스레드의 고정 또는 상대적으로 고정 된 반복 번호를 사용 . 한편으로 이러한 장점, 스레드가 시스템 리소스 오버 헤드의 빈번한 스레드 생성과 소멸을 제거하는 반면에,는 얼굴 과량의 과제는 점진적인 열화를 제출할 수있다 .
5 자바 장 잠금
5.1 잠금 인터페이스
- 그리고 차이를 동기화 :
- 암시 취득 누락 잠금 해제 (또는 동기 블록에 의해 제공되는 방법에 의해) 편의;
- 잠금 획득을 가지고 조작성의 놓습니다. 인터럽트 잠금 획득 시간 제한 및 동기화 된 키워드 잠금 및 기타 동기화 기능을 얻을 사용할 수 없습니다
Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}
-
마지막으로 잠금을 해제 한 후 출시 될 궁극적으로 잠금을 얻을 수 있는지 확인하고
-
인수가 잠금시 발생하는 것처럼 (사용자 정의 구현 잠금) 이상, 예외가 발생, try 블록을 작성하는 과정에서 고정되지 마십시오, 그것은 또한 아무 이유없이 잠금의 해제로 이어질 것
4. 특징 :
- 잠금을 얻기위한 시도를 비는 차단 : 현재 스레드를 잠금을 획득하는 잠금이 성공적으로 획득하고 잠금을 또 다른 스레드에 의해 인수되지 않은이 시점 경우;
- 잠금을 획득 중단 될 수 :
- 잠금을 취득 제한 시간 :
5.2 큐 동기화
AbstractQueuedSynchronizer의 (AQS) - 동기화
주요 이용 동기가 상속 동기를 통해, 서브 클래스를 상속 및 동기화 상태를 관리 할 수있는 자사의 추상 메소드를 구현되고, 필연적으로 추상 메소드의 구현 과정에서 동기화 상태를 변경해야합니다, 당신은 제공된 사용 동기에 필요 세 가지 방법이 작동합니다. 이렇게 동기화 동기화 구성 요소의 다른 유형의 구현을 용이하게하기 위해, 또한, 동기 상태로의 공유 액세스를 지원할 수있는 동기화 된 상태로 둘 배타적 액세스를 지원한다 (ReentrantLock와, ReentrantReadWriteLock, CountDownLatch를)
5.3 재진입 잠금
로크를 획득하는 경우 로크는 리소스의 나사 체결 복제를 지원할 수 있음을 나타내고, 추가로, 또한 공정과 불공정 선택을 지원한다.
공정한 액세스 잠금 : 그 자물쇠에 긴 대기 스레드 우선 순위 액세스입니다.
5.4 읽기 - 쓰기 잠금
5.5 LockSupport 도구
5.6 조건 인터페이스
제 6 장 자바 동시 용기 및 프레임 워크
원리 및 응용 6.1 ConcurrentHashMap의 구현
스레드 안전하고 효율적인 HashMap의
6.1.1 왜 사용 하시겠습니까?
동시 프로그래밍에서 사용 되는 HashMap 무한 루프가 발생할 수 및 사용 스레드 안전 해시 비효율적 .
- 스레드 안전의 HashMap
멀티 스레딩은 풋 동작을 해결하기 위해 100 %의 CPU 사용률의 결과, 무한 루프를 야기 할 것이다. 엔트리리스트 데이터 환형 구조를 형성 HashMap의 리드.
- 비효율적 인 해시
보안 스레드를 보장하기 위해 동기화하지만, 스레드 경쟁 인센티브와 비효율적 인 사용;
- ConcurrentHashMap의 유효 속도를 향상시킬 수있는 고정 세그먼트에 대한 동시 액세스 기술
먼저, 데이터에 의해 세그먼트에 저장 하고를 자물쇠와 데이터마다 최종 잠금 액세스 데이터 중 하나를, 다른 데이터 세그먼트가 다른 스레드가 액세스 할 수있는 경우, 스레드는 보유하고있다.
6.1.2 ConcurrentHashMap의 구조
세그먼트 데이터 구조는 ConcurrentHashMap의 HashEntry 및 데이터 구조입니다. 세그먼트는 반복적 인 잠금 , 잠금 역할 키 데이터를 기억 HashEntry .
ConcurrentHashMap의 각 HashEntry는 링크드리스트 구조의 소자이며, 세그먼트 A 세그먼트의 배열은 HashEntry 어레이를 포함 포함하는 HashEntry 어레이 요소 지키고 각 세그먼트 어레이 HashEntry의 요소가 변경 될시에는, 먼저 획득해야 자신의 해당 세그먼트 잠금 .
6.1.3 ConcurrentHashMap의 초기화
-
배열 초기화 세그먼트
-
초기화 segmentShift 및 segmentMask
-
- 16까지 segmentShift
- 65535까지 segmentMask
-
각 세그먼트를 초기화
6.1.4 위치 세그먼트
로크 세그먼트 ConcurrentHashMap의 세그먼트가 그 데이터의 다른 부분을 보호하고, 사용하기 때문에 소자의 삽입이, 제 해시 알고리즘을 통해 세그먼트를 찾을 때한다.
목적 다시 해시 : 요소가 균일하여 액세스 용기의 효율성을 향상, 다른 세그먼트에 배포 할 수 있도록하는 것이, 해시 충돌을 줄일 수 있습니다.
위치 세그먼트
최종 세그먼트 <K, V> segmentFor (INT 해시) {창 세그먼트 [(해시 >>> segmentShift) segmentMask]; }
6.1.5 ConcurrentHashMap의 작업
- 작업을 얻을
public V get(Object key) { int hash = hash(key.hashCode()); return segmentFor(hash).get(key,hash); }
整个过程不用加锁,除非读到空值才会加锁重读。
原因: get 方法里将要使用到的共享变量都定义为 volatile 类型,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get 操作里只需要读不需要写共享变量 count 和 value,所以可以不用加锁。
( hash >>> segmentShift ) & segmentMask // 定位 Segment 使用的 hash 函数 int index = hash & (tab.length - 1) // 定位 HashEntry 使用的 hash 函数
- put 操作
要写数据,在操作共享变量时,一定要加锁。
两个步骤:
- 判断是否需要对 Segment 里的 HashEntry 数组进行扩容
- 定位添加元素的位置,然后将其放在 HashEntry 数组里
(1)是否需要扩容
在插入元素前,先判断 Segment 里的 HashEntry 数组是否超过阈值(threshold),若超过,就对 HashEntry 数组扩容。(HashMap 是在插入后对判断是否已经达到容量,再进行扩容)
(2)如何扩容
创建一个容量为原来2倍的数组,然后将原数组里面的元素进行再散列后插入到新的数组里面。ConcurrentHashMap 只对某个 Segment 进行扩容,更高效。
- size 操作
先尝试2次通过不锁住 Segment 的方式来统计各个 Segment 大小,如果统计过程中,容器的 count 发生变化,则采用**加锁的方式(把所有的 Segment 的put、get 和clean方法全部锁住)**来统计所有 Segment 的大小。
6.2 ConcurrentLinkedQueue
实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队公用一把锁)或两把锁(出入队用不同锁)的方式来实现。非阻塞的算法可以用循环 CAS 的方式来实现。
ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全队列,采用 FIFO 的规则对节点进行排序。
6.2.2 入队列
- 入队列的过程
将入队节点添加到队列的尾部。入队过程两件事儿:①定位出尾结点;②使用 CAS 算法将入队节点设置成尾结点的 next 节点,如不成功则重试。
- 定位尾结点
tail 节点并不总是尾结点,所以每次入队都需要通过 tail 节点来找到尾结点
- 设置入队节点为尾结点
- HOPS 的设计意图
控制并减少 tail 节点的更新频率,而不是每次节点入队后都将 tail 节点更新成尾结点,而是大于等于常量 HOPS 时才更新,提高了入队的效率。
6.2.3 出队列
也是通过设置 HOPS 来确定什么时候更新 head 节点,提高出队效率。
6.3 Java 中的阻塞队列
6.3.1 什么是阻塞队列
BlockingQueue:
- 阻塞插入: 队列满时,阻塞插入元素的线程,直至不满;
- 阻塞移除: 队列空时,阻塞移除元素的线程,直至不空。
方法/处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add ( e ) | offer ( e ) | put ( e ) | offer ( e, time, unit) |
移除方法 | remove ( ) | poll ( ) | take ( ) | poll ( time, unit) |
检查方法 | element ( ) | peek ( ) | 不可用 | 不可用 |
6.3.2 Java 里的阻塞队列
JDK 7:
- ArrayBlockingQueue
数组实现的有界阻塞队列。默认不保证线程访问的公平性。为了保证公平性,可能降低吞吐量。
- LinkedBlockingQueue
链表实现的有界阻塞队列。默认和最大长度为 Integer.MAX_VALUE
- PriorityBlockingQueue
支持优先级的无界阻塞队列。
- DelayQueue
支持延时获取元素的无界阻塞队列。使用优先队列实现
- SynchronousQueue
不存储元素的阻塞队列,每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素。吞吐量高于 LinkedBlockingQueue 和 ArrayBlockingQueue
- LinkedTransferQueue
链表结构的无界阻塞队列
- LinkedBlockingDeque
链表结构,双向阻塞。可运用在“工作窃取”模式中。
6.3.3 阻塞队列的实现原理
使用通知模式实现。
6.4 Fork/Join 框架
6.4.1 是什么?
是把一个大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
6.4.2 工作窃取算法
是指从某个线程(这个线程自己的活儿干完了,跑去帮别人干活儿)从其他队列里窃取任务来执行。
使用双端队列,被窃取线程从头部拿任务执行,窃取线程从尾部拿任务执行。
优点:
- 充分利用线程进行并行运算,减少了线程间的竞争。
缺点:
- 在某些情况下还是存在竞争,比如队列中只有一个任务时。且该算法会消耗更多的系统资源,比如创建多个线程和多个双端队列。
6.4.3 框架设计
使用两个类完成分割任务,执行任务并合并结果。
①ForkJoinTask:创建ForkJoin 任务
两个子类(用时继承):
- RecursiveAction: 用于没有返回结果的任务
- RecursiveTask: 用于有返回结果的任务
②ForkJoinPool:ForkJoinTask 需要通过 ForkJoinPool 来执行。
Chapter 7 Java 中的13个原子操作类
7.1 原子更新基本类型类
- AtomicBoolean
- AtomicInteger
- AtomicLong
7.2 原子更新数组
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
- AtomicIntegerArray
7.3 原子更新引用类型
- AtomicReference
- AtomicReferenceFiledUpdater
- AtomicMarkableReference
7.4 原子更新字段类
- AtomicIntegerFiledUpdater
- AtomicLongFiledUpdater
- AtomicStampedReference
Chapter 8 Java 中的并发工具类
CountDownLatch、CyclicBarrier和Semphore 并发流程控制;
Exchanger 线程间交换数据
8.1 等待多线程完成的 CountDownLatch
允许一个线程或多个线程等待其他线程完成操作。
CountDownLatch 的构造函数接收一个 int 类型的参数作为计数器,如果你想等待 N 个点完成,这里就传入 N。调用 countDown 方法,N 就减1。CountDownLatch 的 await 方法会阻塞当前线程,直到 N = 0。N 个点,可以是 N 个线程,也可以是1个线程里面的 N 个执行步骤。用在多线程里面,只需要吧 CountDownLatch 的引用传递到线程里即可。
CountDownLatch 不能重新初始化或修改 CountDownLatch 对象的内部计数器的值。
8.2 同步屏障 CyclicBarrier
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被拦截的线程才会继续运行(继续执行的线程没有先后顺序,取决于CPU调度)。
8.2.1 简介
默认构造方法是 CyclicBarrier (int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达屏障,然后当前线程被阻塞。
还有一个高级构造方法 CyclicBarrier (int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景。
8.2.3 CyclicBarrier 与 CountDownLatch 的区别
CountDownLatch 的计数器只能使用一次,但是 CyclicBarrier 的计数器可以使用 reset()方法重置,因此 CyclicBarrier 能处理更为复杂的业务场景。
8.3 控制并发线程数的 Semaphore
用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理使用公共资源。
- 应用场景
做流量控制,特别是公共资源有限的应用场景,比如数据库连接。
8.4 线程间交换数据的 Exchanger
用于线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以通过 exchange 方法交换彼此的数据。
可以应用于遗传算法。
可以应用于校对工作。
Chapter 9 Java中的线程池
好处:
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性。
9.1 线程池的实现原理
处理流程中,先判断核心线程池是否已满,满了再判断工作队列是否已满,满了再判断线程池是否已满,满了再执行拒绝策略。
ThreadPoolExecutor 执行 execute()方法:
四种情况分析:
- 若当前运行的线程少于 coorPoolSize,则创建新线程来执行任务(需要获得全局锁,开销大)
- 若运行的线程大于等于 coorPoolSize,则将任务加入 BlockingQueue(大多数时候处于这里)
- 若 BlockingQueue 队列已满,则创建新的线程来处理任务(需要获得全局锁,开销大)
- 若创建新线程将使当前运行的总线程数超出 maximumPoolSize,那么执行拒绝策略。
工作线程: 线程池创建线程时,会将线程封装成工作线程 Worker,Worker在执行完任务之后还会循环获取工作队列里的任务来执行。
9.2 线程池的使用
9.2.1 线程池创建
通过 ThreadPoolExecutor 创建,很多个参数。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, millseconds, runnableTaskQueue, handler)
9.2.2 向线程池提交任务
execute() // 提交不需要返回值的任务,无法判断任务是否被线程池执行成功 submit() // 提交需要返回值的任务
submit(): 线程池会返回一个 future 类型的对象,通过这个对象可以判断任务是否执行成功,且可以通过 future 的 get 方法获取返回值,get 方法会阻塞当前线程直到任务完成,使用 get(long timeout, TimeUnit unit) 会阻塞一段时间后立即返回,这时任务可能还没有执行完成。
9.2.3 关闭线程池
종료 () 또는 shutdownNow의 () 메소드.
원리는 스레드 풀의 모든 작업자 스레드를 통과하는 것입니다, 나는이 작업을 멈추지 않을 수 있습니다 인터럽트 응답 할 수 있도록 다음 하나의 방법 인터럽트 하나 개의 호출에 의해, 스레드를 중단 할 수 있습니다.
일반적으로 스레드 풀을 종료 종료를 사용합니다.