마스터 제어 스레드 풀 스레드 마법

1. 왜 당신은 스레드 풀을 필요합니까

공정 효율을 개선하기 위해 CPU 성능을 최대한 활용하기 위해 매우 빠르게 컴퓨터 케이스의 속도를 계산 오늘날의 CPU에서 우리는 프로그램에서 스레드를 사용합니다. 그러나 자주 이렇게 때문에 자원을 관리하고 스레드 풀의 도입에 소비 속성의 스레드 스레드 생성과 파괴를 줄이기 위해, 프로그램의 실행 속도을 방해 위장, 높은 동시성 및 파괴 스레드에서 만들었습니다.

스레드 풀의 사용 2. 아래에서 어떤 시나리오

서버가 많은 작업을 받으면 당신은 스레드 풀을 사용하는 경우 프로그램의 효율성을 향상 시키도록 크게, 스레드 생성과 파괴의 수를 줄일 수 있습니다
실제 개발에 당신이 5 개 이상의 스레드를 생성해야하는 경우 다음 관리 스레드 풀을 사용할 수 있습니다,

3. 소개 스레드 풀 매개 변수 및 특성

파일
https://user-gold-cdn.xitu.io/2020/1/12/16f9a146e43c9ba2?imageView2/0/w/1280/h/960/format/webp/ignore-error/1

3.1 corePoolSize를하고 maxPoolSize

corePoolSize를 : 스레드 풀을 생성하고, 어떤 스레드가 작업이 곧 다시 스레드를 만들 때만이되어 있지 않은 경우.

maxPoolSize는 : 스레드 풀 스레드의 커널 스레드의 수를 기준으로 몇 가지 여분을 추가 할 수 있지만 스레드의 수가 maxPoolSize입니다 제한 할 수 있습니다. 예를 들어, 작업의 구현의 첫 날은 거의 작업은 다음 날 실시하지만, maxPoolSize 매개 변수를 사용하여, 당신은 유연성 태스킹을 향상시킬 수있는 매우 크다.

규칙 3.2 추가 스레드

스레드에서 스레드의 수가 적은 corePoolSize를보다 경우에도 작업이 새 스레드를 생성하는 것입니다 수행합니다.
스레드의 수는 동일 (또는 그 이상) corePoolSize를, 그러나보다 적은 태스크 큐 maxPoolSize에 배치 될 경우.
큐가 가득하고, 스레드의 수가 적은 maxPoolSize 이상이면 작업을 실행하기 위해 새로운 스레드를 생성합니다.
큐가 가득하고, 스레드의 수보다 크거나 maxPoolSize 같은 경우 작업이 거부됩니다.
실행 흐름 :
파일
https://user-gold-cdn.xitu.io/2020/1/12/16f9a29c384d9962?imageView2/0/w/1280/h/960/format/webp/ignore-error/1

스레드의 특성 3.3 변경

corePoolSize를하고 maxPoolSize 다음은 고정 된 크기의 스레드 풀을 만드는 것이며, 같은 값으로 설정.
우리는 스레드 풀 적은 스레드 수를 유지하려는, 그리고이 증가하는 경우에만 부하가 커지게된다.
스레드 풀 maxPoolSize 파라미터가 큰 값, 예를 들어 Integer.MAX_VALUE로 설정되는 경우, 스레드 풀은 동시 작업의 수를 수용하기 위해 허용 될 수있다.
corePoolSize를를 만드는 스레드의 수를 초과하지 않는 : 큐가 스레드를 만들 수있는 완전한 뜻이 만 할 때 (LinkedBlockingQueue 등 등) 안 바운드 대기열을 사용 그렇다면, corePoolSize를보다 큽니다.

3.4이 KeepAliveTime

스레드 풀은 corePoolSize를이 초과 스레드 유휴 시간이 KeepAliveTime보다 큰 그렇다면, 그들은 종료됩니다 thread의 현재의 수보다 큰 경우.

사용이 KeepAliveTime 매개 변수는 자원 소비 할 때 너무 많은 스레드의 중복을 줄일 수 있습니다.

3.5 ThreadFactory를

새로운 스레드가 동일한 스레드 그룹의 스레드 밖으로 만든 ThreadFactory를 기본 Executors.defaultThreadFactory ()에 의해 생성되고, 같은 NORM_PRIORITY 우선 순위가 아닌 데몬 스레드를 가지고있다. 당신이 ThreadFactory를 지정 소유하고 있다면, 당신은 데몬 스레드 등인지, 스레드 이름, thread 그룹, 우선 순위를 변경할 수 있습니다. DefaultThreadFactory 직접 정상적인 상황에서 라인에.

3.6이 Workqueue

직접 전송 (SynchronousQueue는) 약간의 작업은, 단지 교통 큐의 간단한 작업을 사용하여이 작업의 큐는 큐의 사용에 저장 될 수없는, maxPoolSize는 크게 설정해야합니다.

무제한으로 큐 (LinkedBlockingQueue 등) : 당신이 Workqueue로, 훨씬 maxQueue 설정, 바운드 형식의 큐를 사용하는 장점은 교통의 분출을 방지하는 것입니다 사용되지 않습니다 바운드 형식의 큐를 사용하는 경우 속도 처리 작업 제출 작업을 유지할 수없는 경우, 단점은 속도입니다 이는 OOM 예외 결과 작업 더 바운드 대기열로 이어질 것이다.

경계 큐 (ArrayBlockingQueue 등) : maxPoolSize 의미있는 사용 경계 큐는, 그래서 스레드 풀을 큐의 크기를 설정할 수 있습니다.

4. 스레드 풀 수동 또는 자동으로 생성 작성해야

그것은 우리가 더 나은, 스레드 풀의 운영 규칙을 이해하기 위해 자원 고갈의 위험을 방지 할 수 있기 때문에 수동으로 더 잘 만들 수 있습니다.

직접 포장 4.1 JDK 가져올 것이다 질문 스레드 풀을 호출

인 newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

동일한 corePoolSize를 통과하고 maxPoolSize 스레드의 수가 고정을 보장 할 수 의해 인 newFixedThreadPool 스레드 풀은이 KeepAliveTime의 0L는 시간 파괴 나타내는 바운드 대기열이 Workqueue 사용이다. 이러한 잠재적 인 문제는 작업 제출 시간의 비율 뒤에 작업을 처리하는 속도가,이 OOM 예외의 원인이 Workqueue에서 작업을 축적를 많이 만들 수 있습니다 때.

4.2 데모 인 newFixedThreadPool 메모리 오버 플로우 문제

/**
 * 演示newFixedThreadPool线程池OOM问题
 */
public class FixedThreadPoolOOM {

    private static ExecutorService executorService = Executors.newFixedThreadPool(1);

    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executorService.execute(new SubThread());
        }
    }
}

class SubThread implements Runnable {

    @Override
    public void run() {
        try {
            //延长任务时间
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

JVM 인수를 변경
파일

운영 결과
파일

4.3 newSingleThreadExecutor

스레드 풀 스레드 이름을 사용하여 인쇄

public class SingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }
}

파일

보기 newSingleThreadExecutor 소스

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

从源码可以看出newSingleThreadExecutor和newFixedThreadPool基本类似,不同的只是corePoolSize和maxPoolSize的值,所以newSingleThreadExecutor也存在内存溢出问题。

4.4 newCachedThreadPool

newCachedThreadPool也被称为可缓存线程池,它是一个无界线程池,具有自动回收多余线程的功能。
파일

public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }
}

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

파일
newCachedThreadPool的maxPoolSize设置的值为Integer.MAX_VALUE,所以可能会导致线程被无限创建,最终导致OOM异常。

4.5 newScheduledThreadPool

该线程池支持周期性任务的执行

public class ScheduledThreadPoolTest {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService =
                Executors.newScheduledThreadPool(10);
//        scheduledExecutorService.schedule(new Task(), 5, TimeUnit.SECONDS);
        scheduledExecutorService.scheduleAtFixedRate(new Task(), 1, 3, TimeUnit.SECONDS);
    }
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

파일

4.6 正确的创建线程池的方法

根据业务场景不同,自己设置线程池参数,例如内存有多大,自己取线程名子等。

4.7 线程池里的线程数量设置多少比较合适?

CPU密集型(加密、计算hash等):最佳线程数设置为CPU核心数的1——2倍。
耗时I/O型(读写数据库、文件、网络读写等):最佳线程数一般会大于CPU核心数很多倍,以JVM监控显示繁忙情况为依据,保证线程空闲可以衔接上。参考Brain Goezt推荐的计算方法:

线程数=CPU核心数 × (1+平均等待时间/平均工作时间)

5.对比线程池的特点

파일

FixedThreadPool:通过手动传入corePoolSize和maxPoolSize,以固定的线程数来执行任务

SingleThreadExecutor:corePoolSize和maxPoolSize默认都是1,全程只以1条线程执行任务

CachedThreadPool:它没有需要维护的核心线程数,每当需要线程的时候就进行创建,因为它的线程存活时间是60秒,所以它也凭借着这个参数实现了自动回收的功能。

ScheduledThreadPool:这个线程池可以执行定时任务,corePoolSize是通过手动传入的,它的maxPoolSize为Integer.MAX_VALUE,并且具有自动回收线程的功能。

5.1 为什么FixedThreadPool和SingleThreadExecutor的Queue是LinkedBlockingQueue?

因为这两个线程池的核心线程数和最大线程数都是相同的,也就无法预估任务量,所以需要在自身进行改进,就使用了无界队列。

5.2 为什么CachedThreadPool使用的Queue是SynchronousQueue?

因为缓存线程池的最大线程数是“无上限”的,每当任务来的时候直接创建线程进行执行就好了,所以不需要使用队列来存储任务。这样避免了使用队列进行任务的中转,提高了执行效率。

5.3 为什么ScheduledThreadPool使用延迟队列DelayedWorkQueue?

因为ScheduledThreadPool是延迟任务线程池,所以使用延迟队列有利于对执行任务的时间做延迟。

5.4 JDK1.8中加入的workStealingPool

workStealingPool适用于执行产生子任务的环境,例如进行二叉树的遍历。
workStealingPool具有窃取能力。
使用时最好不要加锁,而且不保证执行顺序。

6.停止线程池的正确方法

shutdown:调用了shutdown()方法不一定会立即停止,这个方法仅仅是初始整个关闭过程。因为线程池中的线程有可能正在运行,并且队列中也有待处理的任务,不可能说停就停。所以每当调用该方法时,线程池会把正在执行的任务和队列中等待的任务都执行完毕再关闭,并且在此期间如果接收到新的任务会被拒绝。

/**
 * 演示关闭线程池
 */
public class ShutDown {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);
        executorService.shutdown();

        //再次提交任务
        executorService.execute(new ShutDownTask());
    }
}

class ShutDownTask implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}

파일

isShutdown:可以用于判断线程池是否被shutdown了

/**
 * 演示关闭线程池
 */
public class ShutDown {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);
        System.out.println(executorService.isShutdown());
        executorService.shutdown();
        System.out.println(executorService.isShutdown());
        //再次提交任务
//        executorService.execute(new ShutDownTask());
    }
}

class ShutDownTask implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}

파일

isTerminated:可以判断线程是否被完全终止了

/**
 * 演示关闭线程池
 */
public class ShutDown {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);
        System.out.println(executorService.isShutdown());
        executorService.shutdown();
        System.out.println(executorService.isShutdown());
        System.out.println(executorService.isTerminated());
        //再次提交任务
//        executorService.execute(new ShutDownTask());
    }
}

파일
将循环的次数改为100次,并且在第一次调用isTerminated方法的地方休眠10s
파일

awaitTermination:传入等待时间,等待时间达到时判断是否停止了,主要用于检测。

//在3s后判断线程池是否被终止,返回boolean值
System.out.println(executorService.awaitTermination(3L, TimeUnit.SECONDS));

shutdownNow:调用了这个方法时,线程池会立即终止,并返回没有被处理完的任务。如果需要继续执行这里的任务可以再次让线程池执行这些返回的任务。

7.任务太多,怎么拒绝?

7.1 拒绝的时机

当Executor关闭时,新提交的任务会被拒绝。
以及Executor对最大线程数和工作队列容量使用有限边界并且已经饱和时。

7.2 拒绝策略

AbortPolicy(中断策略):直接抛出异常进行拒绝
DiscardPolicy(丢弃策略):不会得到通知,默默的抛弃掉任务
DiscardOldestPolicy(丢弃最老的):由于队列中存储了很多任务,这个策略会丢弃在队列中存在时间最久的任务。
CallerRunsPolicy:比如主线程给线程池提交任务,但是线程池已经满了,在这种策略下会让提交任务的线程去执行。

总结:第四种拒绝策略相对于前三种更加“机智”一些,可以避免前面三种策略产生的损失。在第四种策略下可以降低提交的速度,达到负反馈的效果。

8.使用钩子为线程池加点料(可用于日志记录)

/**
 * 演示每个任务执行的前后放钩子函数
 */
public class PauseableThreadPool extends ThreadPoolExecutor {

    private boolean isPaused;
    private final ReentrantLock lock = new ReentrantLock();
    private Condition unPaused = lock.newCondition();

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                               TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }


    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                               TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }


    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                               TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                               TimeUnit unit, BlockingQueue<Runnable> workQueue,
                               ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        lock.lock();
        try {
            while (isPaused) {
                unPaused.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void pause() {
        lock.lock();
        try {
            isPaused = true;
        } finally {
            lock.unlock();
        }
    }

    public void resume() {
        lock.lock();
        try {
            isPaused = false;
            //唤醒全部
            unPaused.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        PauseableThreadPool pauseableThreadPool = new PauseableThreadPool(10, 20, 10L,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("我被执行");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10000; i++) {
            pauseableThreadPool.execute(runnable);
        }
        Thread.sleep(1500);
        pauseableThreadPool.pause();
        System.out.println("线程池被暂停了");
        Thread.sleep(1500);
        pauseableThreadPool.resume();
        System.out.println("线程池被恢复了");
    }
}

파일

9.线程池实现原理

9.1 线程池组成部分

线程池管理器
工作线程
任务队列
任务

9.2 Executor家族

파일
실행기 : 그것은 최상위 인터페이스, 다른 인터페이스와 클래스 나 상속 또는 다음의 방법을 포함하고, 그 구현은 다음
보이드 (실행 가능한 명령)을 실행;
ExecutorService를을 : 그것은 실행자하는 실행자 하위 인터페이스 상속 및 내부 인터페이스들을 추가 새로운 방법과 같은 하위 섹션 6 언급 한 바와 같이 여러 가지 방법
실행자 :이 클래스는 스레드 풀을 만들 수있는 방법을 포함하는 유틸리티 클래스입니다

9.3 스레드 풀 구현 원리 작업은 다중화

동일한 스레드를 사용하여 다른 작업을 수행

소스 코드 분석

public void execute(Runnable command) {
    // 判断任务是否为空,为空就抛出异常
    if (command == null)
        throw new NullPointerException();
    
    int c = ctl.get();
    // 如果当前线程数小于核心线程数,就增加Worker
    if (workerCountOf(c) < corePoolSize) {
        // command就是任务,点击addWorker方法
        // 第二个参数用于判断当前线程数是否小于核心线程数
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 此时线程数大于等于核心线程数
    // 判断线程池是不是正在运行并将任务放到工作队列中
    if (isRunning(c) && workQueue.offer(command)) {
        // 再次检查线程状态
        int recheck = ctl.get();
        // 如果线程不是正在运行的,就删除掉任务并且拒绝
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)   //这里用于避免已经提交的任务没有线程进行执行
            addWorker(null, false);
    }
    // 如果任务无法添加或者大于最大线程数就拒绝任务
    else if (!addWorker(command, false))
        reject(command);
}

당신이 addWorker으로보고 싶어하기 때문에 노동자는 () () runWorker 노동자 클래스 메소드를 볼 방법을 클릭

w = new Worker(firstTask);

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    // 获取到任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 只要任务不为空或者能够获取到任务就执行下面的方法
        while (task != null || (task = getTask()) != null) {
            w.lock();
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // task是一个Runnable类型,调用run()方法就是运行线程
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

요약 : 핵심 원리는 다른 작업을 수행하기 위해 동일한 목적의 스레드를 달성하기 위해 그래서, 작업 따라서 다중화 스레드를 달성이다라고 run () 메소드 비어 있지 않은 경우, 작업에 도착하는 것입니다.

10. 스레드 풀 상태

RUNNING : 새 작업 및 프로세스를 허용하는 작업 큐에
새 작업을 허용하지 않습니다하지만 처리 작업 대기열 : 종료
STOP을 : 새 작업을 허용하지 않습니다, 대기 처리하지 작업 및 인터럽트 작업이 수행되고, 가지고에 shutdownNow의 ()를 호출하는 것입니다 효과
정리가 : 중국 의미가 workerCount 제로, 스레드가 정리 상태로 변환 종료 실행할 때 작업이 종료되었음을 의미, 깨끗한 () 후크 방법은
종료 : 종료 () 작업을 완료
파일

11. 스레드 풀주의 사항

(메모리 오버 플로우가 발생하기 쉬운 축적) 작업의 축적을 방지하기 위해
스레드의 수의 과도한 증가를 방지하기 위해 (캐시 스레드 풀 스레드의 수가 과도하게 증가 할 수 있습니다)
누출 스레드의 조사 (스레드가 완료 있지만 재활용 할 수 없습니다)

전송 : HTTPS : //juejin.im/post/5e1b1fcce51d454d3046a3de

추천

출처www.cnblogs.com/chen-chen-chen/p/12234208.html