스레드 재사용을 실현하고 스레드 재생성 및 파괴를 방지할 수 있습니다. 스레드를 생성하고 스레드를 파괴하는 것은 CPU에 있어서 매우 비용이 많이 듭니다.
생성할 수 있는 최대 스레드 수를 제한하고, 자체 시스템 성능에 따라 스레드 풀 매개변수를 동적으로 조정하여 애플리케이션 성능을 향상시킬 수 있습니다.
예약 실행, 동시성 제어 등의 기능을 제공합니다.
스레드 관리가 통합되었습니다.
1: 캐시 스레드 풀(권장하지 않음)
2: 고정 용량 스레드 풀(권장하지 않음)
3: 단일 스레드 풀(권장하지 않음)
4: 타이밍 작업 스레드 풀(권장하지 않음)
5: ThreadPoolExecutor 구성 방법을 통해 스레드 풀 생성(Alibaba 개발 매뉴얼에서 적극 권장)
스레드 풀을 생성하는 처음 네 가지 방법은 모두 Executors의 정적 메서드를 통해 생성됩니다.
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
캐시된 스레드 풀이 권장되지 않는 이유는 무엇인가요?
소스 코드 분석
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
위의 두 코드 조각을 통해 CachedThreadPool의 maximumPoolSize는 Integer 2147483647의 최대값으로 무제한 스레드 생성과 동일하며, 스레드 생성에는 메모리가 필요하므로 메모리 오버플로가 발생함을 알 수 있습니다. , 그리고 일반 기계는 그렇게 많은 수의 스레드를 생성하기 위해 그렇게 큰 메모리를 사용하지 않습니다.
newFixedThreadPool(int num), num은 지정하려는 스레드의 고정 개수입니다
ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
출력:
pool-1-thread-5
run>4
pool- 1-thread-4run>3
pool-1-thread-5run>5
pool-1-thread-3run>2
pool- 1- 스레드-3run>8
pool-1-thread-3run>9
pool-1-thread-2run>1
pool-1- thread- 1run>0
pool-1-thread-5run>7
pool-1-thread-4run>6
볼 수 있습니다 스레드는 재사용을 위해 사용됩니다.
FixedThreadPool이 고정 스레드 풀인 이유는 무엇인가요?
소스 코드 분석
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
이 소스 코드에서 코어 스레드 수(corePoolSize)와 최대 스레드 수(maximumPoolSize)가 모두 nThread임을 알 수 있습니다. 확장되고 스레드 수가 고정됩니다.
ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
SingleThreadExecutor에는 왜 스레드가 하나만 포함되어 있나요?
소스 코드 분석
public static ExecutorService newSingleThreadExecutor() { return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
이 소스 코드를 보면 코어 스레드 수(corePoolSize)와 최대 스레드 수(maximumPoolSize)가 모두 1이므로 하나의 스레드만 포함되어 있음을 알 수 있습니다.
int initDelay=10; //初始化延时 int period=1;//初始化延迟过了之后,每秒的延时 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"); } },initDelay,period, TimeUnit.SECONDS);
이 코드의 효과는 프로그램이 실행된 후 10초를 기다린 후 첫 번째 결과를 출력하고 1초마다 결과를 출력하는 것입니다.
ScheduledThreadPool이 권장되지 않는 이유는 무엇입니까?
소스 코드 분석
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue()); }
ScheduledThreadPool의 최대 스레드 수(maximumPoolSize)는 Integer 2147483647의 최대값임을 알 수 있는데, 이는 무제한 스레드 생성과 동일하며, 스레드를 생성하려면 메모리가 필요하므로 메모리 오버플로가 발생합니다. , 그리고 일반적으로 머신은 그렇게 많은 수의 스레드를 생성하기 위해 그렇게 큰 메모리를 사용하지 않습니다.
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 12; i++) { final int finalI = i; threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { }
corePoolSize의 7개 매개변수에 대한 자세한 설명: 코어 스레드 수. 이러한 스레드는 일단 생성되면 소멸되지 않고 항상 존재합니다. 스레드 풀에는 기본적으로 스레드가 없습니다. 작업이 도착하면 ThreadFactory를 통해 스레드가 생성되고 항상 존재합니다.
maximumPoolSize: 최대 스레드 수입니다. 비코어 스레드 수 = maximumPoolSize-corePoolSize 비코어 스레드 수는 실제로 확장 가능한 스레드 수이며 삭제될 수 있습니다.
keepAliveTime: 비코어 스레드의 유휴 생존 시간입니다. 확장을 통해 생성된 비핵심 스레드 수가 keepAliveTime 이후에도 여전히 유휴 상태인 경우 이러한 비핵심 스레드는 삭제됩니다.
unit: keepAliveTime의 시간 단위, 예: 초
workQueue: 대기 영역. 작업 > corePoolSize가 오면 해당 작업은 workQueue의 차단 대기열에 저장되어 다른 스레드가 처리될 때까지 기다립니다.
threadFactory: 스레드 팩토리. 스레드를 생성하는 방법입니다.
처리자: 거부 전략. > 최대 스레드 수 + workQueue의 용량이 되면 거부 정책이 실행됩니다
ArrayBlockingQueue: 제한된 차단 대기열. 대기열에는 크기 제한이 있습니다. 용량을 초과하면 확장 또는 거부 정책이 트리거됩니다.
public ArrayBlockingQueue(int capacity) { this(capacity, false); }
LinkedBlockingQueue: 무제한 차단 큐, 큐에는 크기 제한이 없으며 메모리 오버플로가 발생할 수 있습니다.
public LinkedBlockingQueue() { this(2147483647); }
AbortPolicy: 직접 예외를 발생시킵니다.
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
DiscardPolicy: 작업이 수행되지 않습니다. 작업을 자동으로 삭제
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
DiscardOldestPolicy: 가장 긴 기존 작업을 삭제합니다
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
CallerRunsPolicy: 작업을 제출한 스레드가 작업을 처리하도록 합니다
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
threadFactory
ThreadFactory threadFactory = Executors.defaultThreadFactory(); threadFactory.newThread(new Runnable() { @Override public void run() { System.out.println("threadFactory"); } }).start();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 26; i++) { //并发数26 final int finalI = i; threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI); } }); } /** * 核心线程数=10,最大线程数=20,故可扩容线程数=20-10 * BlockingQueue的大小为5,故等待区的大小为5,也就是当并发数<=核心线程数+5不会扩容,并发数大于16才会扩容 * * 触发扩容:并发数>核心线程数+阻塞队列的大小 * 对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容 * 因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略 */
为什么这段代码会触发拒绝策略
对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容,但是又因为因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略。
怎么触发扩容
触发扩容:并发数>核心线程数(corePoolSize)+阻塞队列(workQueue)的大小
使用Java纯手写一个线程池
위 내용은 Java 스레드 풀을 만드는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!