[안드로이드] 안드로이드에 대한 심층적 인 이해 : 원리 분석의 스레드 풀을 사용하여

이 열은 큰 지식을 공유 박쥐 인터뷰, 후속는 우려를 클릭 그것이 문제처럼, 계속 업데이트됩니다 초점을 맞추고

1. 소개

스레드 풀은 간단하게 생성하고 자주 가져 스레드를 파괴의 오버 헤드를 피하기 위해, 우리는 쉽게 스레드를 다시 사용할 수있는 스레드 풀을 사용하여 스레드의 집합으로 볼 수있다. 응용 프로그램에서 스레드 풀 백 엔드 관련 서비스에 사용할 수 있습니다. 이러한 등등 웹 서버, 데이터베이스 서버, 등. 웹 서버가 짧은 HTTP 요청의 큰 숫자를받는 경우이 시간에 우리는 단순히 각 HTTP 요청에 대한 처리 스레드를 만들 경우 웹 서버는, 예를 들어, 서버의 자원은 곧 고갈 될 것이다. 물론, 우리는 또한 관리하고 스레드가 자원의 소비를 제한하기 위해 생성 된 재사용 소유 할 수 있지만, 프로그램 로직이 복잡하게 사용합니다. 다행히, 다행히도, 우리는 할 필요가 없습니다. JDK 1.5에서 공식는 강력한 도구 스레드 풀 클래스를 제공하고 있습니다. 이러한 도구를 사용하여, 우리는 저렴한 가격과 멀티 스레딩 기술을 사용할 수 있습니다.

동시 수업을위한 중요한 도구로 Java 스레드 풀의 기준으로 사용됩니다, 나는 스레드 풀의 관련 원칙에 대해 배울 필요가있다 생각합니다. 결국, 스레드 풀 스레드 관리뿐만 아니라 관리 작업뿐만 아니라뿐만 아니라 통계 기능이 있습니다. 그래서 조금 이해하기 위해서는 여전히 자신의 시야를 확장 할 수 있습니다뿐만 아니라 스레드 풀에 익숙해 될 수 있습니다.

2. 상속 시스템

관련 스레드 풀은 인터페이스와 시스템이 비교적 간단 상속 클래스를 많이하지 않습니다. 상속에 다음과 같습니다 관련 :

상술 한 바와 같이, 상위 레벨 인터페이스 실행자 단일 방법 execute. ExecutorService를 부모 클래스 인터페이스를 포함하는 인터페이스를 기반으로하지만, 문에 국한되지 shutdown, submit, invokeAll, invokeAny 등이있다. ScheduledExecutorService를 인터페이스로서, 상기 타이밍과 같은 방법과 관련된 다수의 작업 선언하는  schedulescheduleAtFixedRate. 핵심 스레드 풀은 가능한 ThreadPoolExecutor 클래스 달성, 우리는 실행자 전화 사용 newFixedThreadPool, newSingleThreadExecutor그리고 newCachedThreadPool스레드 풀을 만들 수있는 ThreadPoolExecutor 유형 다른 방법을.

위의 상속 시스템 스레드 풀의 간략한 소개는 여기에 모두가 스레드 풀의 광범위한 윤곽의 일부 이해를 할 수 있습니다. 다음으로, 읽어 계속 스레드 풀 구현 원리를 소개합니다.

3. 원리 분석

핵심 매개 변수의 3.1 분석

핵심 매개 변수 3.1.1 소개

위에서 언급 한 섹션에 ThreadPoolExecutor 클래스의 핵심 스레드 풀의 구현입니다. 코어 클래스는 여러 속성이 생성자에서 초기화 할 수 있습니다 포함되어 있습니다. 다음과 같은 핵심 특성을 소개하기 전에, 우리는 가능한 ThreadPoolExecutor 생성자를 살펴 :

공용 가능한 ThreadPoolExecutor (INT corePoolSize를, 
                          INT maximumPoolSize를, 
                          긴 KeepaliveTime은, 
                          TimeUnit와 유닛 
                          의 BlockingQueue <Runnable를> Workqueue는, 
                          ThreadFactory를 ThreadFactory를, 
                          RejectedExecutionHandler 핸들러)

  

위와 같이, 그 핵심 인자 생성자 매개 변수, 여기에 내가 간단히 각 매개 변수의 중요성을 설명하기 위해 테이블을 사용합니다. 다음과 같습니다 :

매개 변수 설명
corePoolSize를 스레드의 핵심 번호. 값이 스레드의 수보다 적은 경우, 스레드 풀은 새로운 작업을 수행 할 수있는 새로운 스레드를 생성하는 우선 순위를 부여합니다
maximumPoolSize를 스레드 풀 스레드의 최대 수는 유지 될 수있다
이 KeepAliveTime 유휴 스레드의 생존 기간
이 Workqueue 캐시로 수행하는 작업에 대한 작업 대기열
ThreadFactory를 스레드 공장. 그것은 새로운 스레드에 대한 공장보다 의미있는 이름으로 설정할 수 있습니다
매니저 거부 정책. 작업 대기열 및 스레드 풀은 포화 점에있을 때, 새로운 작업을 처리 할 수있는 전략을 사용하지 않았다. 기본값은 즉 예외를 throw 직접, AbortPolicy입니다

다음은 몇 가지 매개 변수에 대해, 읽어 이하 나는 상세하게 설명한다, 각 매개 변수의 간단한 소개입니다.

3.1.2 스레드 생성 규칙

在 Java 线程池实现中,线程池所能创建的线程数量受限于 corePoolSize 和 maximumPoolSize 两个参数值。线程的创建时机则和 corePoolSize 以及 workQueue 两个参数有关。下面列举一下线程创建的4个规则(线程池中无空闲线程),如下:

  1. 线程数量小于 corePoolSize,直接创建新线程处理新的任务

  2. 线程数量大于等于 corePoolSize,workQueue 未满,则缓存新任务

  3. 线程数量大于等于 corePoolSize,但小于 maximumPoolSize,且 workQueue 已满。则创建新线程处理新任务

  4. 线程数量大于等于 maximumPoolSize,且 workQueue 已满,则使用拒绝策略处理新任务

简化一下上面的规则:

序号 条件 动作
1 线程数 < corePoolSize 创建新线程
2 线程数 ≥ corePoolSize,且 workQueue 未满 缓存新任务
3 corePoolSize ≤ 线程数 < maximumPoolSize,且 workQueue 已满 创建新线程
4 线程数 ≥ maximumPoolSize,且 workQueue 已满 使用拒绝策略处理

3.1.3 资源回收

考虑到系统资源是有限的,对于线程池超出 corePoolSize 数量的空闲线程应进行回收操作。进行此操作存在一个问题,即回收时机。目前的实现方式是当线程空闲时间超过 keepAliveTime 后,进行回收。除了核心线程数之外的线程可以进行回收,核心线程内的空闲线程也可以进行回收。回收的前提是allowCoreThreadTimeOut属性被设置为 true,通过public void allowCoreThreadTimeOut(boolean) 方法可以设置属性值。

3.1.4 排队策略

如3.1.2 线程创建规则一节中规则2所说,当线程数量大于等于 corePoolSize,workQueue 未满时,则缓存新任务。这里要考虑使用什么类型的容器缓存新任务,通过 JDK 文档介绍,我们可知道有3中类型的容器可供使用,分别是同步队列有界队列无界队列。对于有优先级的任务,这里还可以增加优先级队列。以上所介绍的4中类型的队列,对应的实现类如下:

实现类 类型 说明
SynchronousQueue 同步队列 该队列不存储元素,每个插入操作必须等待另一个线程调用移除操作,否则插入操作会一直阻塞
ArrayBlockingQueue 有界队列 基于数组的阻塞队列,按照 FIFO 原则对元素进行排序
LinkedBlockingQueue 无界队列 基于链表的阻塞队列,按照 FIFO 原则对元素进行排序
PriorityBlockingQueue 优先级队列 具有优先级的阻塞队列

3.1.5 拒绝策略

如3.1.2 线程创建规则一节中规则4所说,线程数量大于等于 maximumPoolSize,且 workQueue 已满,则使用拒绝策略处理新任务。Java 线程池提供了4中拒绝策略实现类,如下:

实现类 说明
AbortPolicy 丢弃新任务,并抛出 RejectedExecutionException
DiscardPolicy 不做任何操作,直接丢弃新任务
DiscardOldestPolicy 丢弃队列队首的元素,并执行新任务
CallerRunsPolicy 由调用线程执行新任务

以上4个拒绝策略中,AbortPolicy 是线程池实现类所使用的策略。我们也可以通过方法public void setRejectedExecutionHandler(RejectedExecutionHandler)修改线程池决绝策略。

3.2 重要操作

3.2.1 线程的创建与复用

在线程池的实现上,线程的创建是通过线程工厂接口ThreadFactory的实现类来完成的。默认情况下,线程池使用Executors.defaultThreadFactory()方法返回的线程工厂实现类。当然,我们也可以通过

public void setThreadFactory(ThreadFactory)方法进行动态修改。具体细节这里就不多说了,并不复杂,大家可以自己去看下源码。

在线程池中,线程的复用是线程池的关键所在。这就要求线程在执行完一个任务后,不能立即退出。对应到具体实现上,工作线程在执行完一个任务后,会再次到任务队列获取新的任务。如果任务队列中没有任务,且 keepAliveTime 也未被设置,工作线程则会被一致阻塞下去。通过这种方式即可实现线程复用。

说完原理,再来看看线程的创建和复用的相关代码(基于 JDK 1.8),如下:

+----ThreadPoolExecutor.Worker.java
Worker(Runnable firstTask) {
    setState(-1);
    this.firstTask = firstTask;
    // 调用线程工厂创建线程
    this.thread = getThreadFactory().newThread(this);
}

// Worker 实现了 Runnable 接口
public void run() {
    runWorker(this);
}

+----ThreadPoolExecutor.java
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock();
    boolean completedAbruptly = true;
    try {
        // 循环从任务队列中获取新任务
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 执行新任务
                    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);
    }
}

  

3.2.2 提交任务

通常情况下,我们可以通过线程池的submit方法提交任务。被提交的任务可能会立即执行,也可能会被缓存或者被拒绝。任务的处理流程如下图所示:

上面的流程图不是很复杂,下面再来看看流程图对应的代码,如下:

+---- AbstractExecutorService.java
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    // 创建任务
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    // 提交任务
    execute(ftask);
    return ftask;
}

+---- ThreadPoolExecutor.java
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    // 如果工作线程数量 < 核心线程数,则创建新线程
    if (workerCountOf(c) < corePoolSize) {
        // 添加工作者对象
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    // 缓存任务,如果队列已满,则 offer 方法返回 false。否则,offer 返回 true
    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);
    }
    
    // 添加工作者对象,并在 addWorker 方法中检测线程数是否小于最大线程数
    else if (!addWorker(command, false))
        // 线程数 >= 最大线程数,使用拒绝策略处理任务
        reject(command);
}

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            // 检测工作线程数与核心线程数或最大线程数的关系
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 创建工作者对象,细节参考上一节所贴代码
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 将 worker 对象添加到 workers 集合中
                    workers.add(w);
                    int s = workers.size();
                    // 更新 largestPoolSize 属性
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 开始执行任务
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

  

上面的代码略多,不过结合上面的流程图,和我所写的注释,理解主逻辑应该不难。

3.2.3 关闭线程池

我们可以通过shutdownshutdownNow两个方法关闭线程池。两个方法的区别在于,shutdown 会将线程池的状态设置为SHUTDOWN,同时该方法还会中断空闲线程。shutdownNow 则会将线程池状态设置为STOP,并尝试中断所有的线程。中断线程使用的是Thread.interrupt方法,未响应中断方法的任务是无法被中断的。最后,shutdownNow 方法会将未执行的任务全部返回。

调用 shutdown 和 shutdownNow 方法关闭线程池后,就不能再向线程池提交新任务了。对于处于关闭状态的线程池,会使用拒绝策略处理新提交的任务。

4.几种线程池

一般情况下,我们并不直接使用 ThreadPoolExecutor 类创建线程池,而是通过 Executors 工具类去构建线程池。通过 Executors 工具类,我们可以构造5中不同的线程池。下面通过一个表格简单介绍一下几种线程池,如下:

静态构造方法 说明
newFixedThreadPool(int nThreads) 构建包含固定线程数的线程池,默认情况下,空闲线程不会被回收
newCachedThreadPool() 构建线程数不定的线程池,线程数量随任务量变动,空闲线程存活时间超过60秒后会被回收
newSingleThreadExecutor() 构建线程数为1的线程池,等价于 newFixedThreadPool(1) 所构造出的线程池
newScheduledThreadPool(int corePoolSize) 构建核心线程数为 corePoolSize,可执行定时任务的线程池
newSingleThreadScheduledExecutor() 等价于 newScheduledThreadPool(1)

关于我

更多信息可以点击关于我 , 非常希望和大家一起交流 , 共同进步

 

추천

출처www.cnblogs.com/1157760522ch/p/11593171.html