Интервью вопросы - статья о Java пула потоков достаточно

В Java интервью, знание пула потоков, хотя и не упоминая, что должно быть задано, но появление частоты очень высоко. По мнению общественности, а номер «программы New Horizons,» сообщение так, чтобы читатель обратно, чтобы написать статью о Java пула потоков, так что эта часть часть здесь это будет основываться на принципах Java пула потоков для достижения и объясняющих соответствующего исходного кода и так далее.

Что такое пул потоков

бассейн Нить многопоточной формы обработки, будут представлены обработки задач пула потоков пула потоков для выполнения задач, сданные управлять.

Для того, чтобы воспользоваться всеми преимуществами многоядерных процессорных ресурсов, то приложение будет использовать многопоточные параллельные / одновременные вычисления, максимальное использование многоядерной производительности приложений.

Представьте себе, если каждый запрос снова выполняется для создания потока для выполнения задач, уничтожая потоки, то сервер будет тратой ресурсов. В случае высокой параллельности, или даже запустить из ресурсов сервера.

Главная роль пула потоков в два раз: дублирование между различными запросами использование нитей, без частого создания потока и разрушения, снизить накладные расходы и ограничить число потоков управления, не создавать слишком много потоков запускать из пространства памяти процесса, уменьшая при этом контексте потока время переключения.

Общие вопросы интервью

  • Разговор о преимуществах Java пула потоков и реализации принципа?
  • Java предоставляет все операнды пул потоков, как поступить?
  • В соответствии с внутренним механизмом пула потоков, при подаче новой задачи, которую следует считать ненормальной?
  • Какие пул потоков имеет рабочую очередь?
  • Используйте неограниченную очередь пул потоков могут привести к памяти парить это?
  • Разговор о некоторых общего пула потоков и использование сценариев?

Создание и использование пула потоков

Увеличение встроенной версии JDK5 пула потоков ThreadPoolExecutor реализации, обеспечивая при этом Executors создавать различные типы пула потоков. Исполнители предоставляются в следующих методах создания общего пула потоков:

  • newSingleThreadExecutor: однопоточное пул потоков. Если результат ненормального конца, затем создать новый, чтобы обеспечить порядок выполнения подачи.
  • newFixedThreadPool: создать пул потоков фиксированного размера. Надбавки нить в соответствии с задачей, представленной до максимума остается неизменной. Если в результате аварийного завершения, создаст новую нить дополнения.
  • newCachedThreadPool: Создание кэшированного пула потоков. Автоматически добавлять или переработаны нить в соответствии с заданием.
  • newScheduledThreadPool: Поддержка регулярных и периодических требования для выполнения задач.
  • newWorkStealingPool: JDK8 добавить, динамически создавать и выключаться в зависимости от требуемого потока параллелизма уровня, используя несколько очередей к снижению конкуренции, использование базового ForkJoinPool достигается. Преимущество, что процессор может использовать нескольких, чтобы разделить задачу на несколько «маленькие задачи», на множество параллельного выполнения на ядре процессора; когда множество «завершение выполнения небольших задач, а затем объединить результаты этих Это может быть.

Предоставляя класс исполнителей в JDK для поддержки более чем один типа пула потоков создаются, но, как правило, не рекомендуется для разработчиков непосредственно использовать (см «Али-Бабу спецификацию разработки Java»).

Исполнители пул потоков не может создавать, но ThreadPoolExecutor образом, этот подход позволяет студентам писать более четкий операционные правила пула потоков, чтобы избежать риска истощения ресурсов.

Часть способа исполнителей недостатков:

  • newFixedThreadPool и newSingleThreadExecutor основной проблемой является накопление очереди на обработку запроса может потреблять очень большой объем памяти, даже OOM.
  • newCachedThreadPool и newScheduledThreadPool: Основная проблема заключается максимальное количество потоков Integer.MAX_VALUE, может создать очень большое количество нитей, даже OOM.

Между тем, Alibaba стандарт разработки Java рекомендуется тремя видами пула потоков, созданных.

Один из вариантов, введение пакета Обще-lang3.

//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());复制代码

Режим 2 введен com.google.guava пакет.

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown复制代码

Три способа, пружинный режим конфигурации пула потоков: пользовательские нити боб растения должно достичь ThreadFactory, ссылка может быть другим интерфейсом класса реализации по умолчанию, использовать прямую инъекцию боб, вызовы выполнить метод (Runnable задачи) может быть.

<bean id="userThreadPool"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="10" />
    <property name="maxPoolSize" value="100" />
    <property name="queueCapacity" value="2000" />

<property name="threadFactory" value= threadFactory />
    <property name="rejectedExecutionHandler">
        <ref local="rejectedExecutionHandler" />
    </property>
</bean>
// in code
userThreadPool.execute(thread);复制代码

ThreadPoolExecutor конструктор

В дополнении к вышесказанному рекомендуемому способу, чтобы создать пул потоков, но и через конструктор ThreadPoolExecutor непосредственно создать пул потоков. В сущности, описанный выше способ, в конечном счете созданный объект ThreadPoolExecutor, а затем укладываются процесс упаковки.

ThreadPoolExecutor предоставляет несколько конструкторов, конструктор мы называем в конце концов объяснить.

 public ThreadPoolExecutor(int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue,
      ThreadFactory threadFactory,
      RejectedExecutionHandler handler) {
   // 省略代码
}复制代码

Ключевую роль аналитических параметров следующим образом:

  • corePoolSize: Ядро Максимальное количество потоков в пуле потоков.
  • maximumPoolSize: Максимальное количество нитей размера пула потоков.
  • KeepAliveTime: нить размер пула нежизнеспособного времени основного потока простоя.
  • единица: холостые единицы времени выживания нити.
  • workQueue: хранение блокирующих задач очереди.
  • threadFactory: Создать новую нить фабрику, все потоки создаются на заводе, есть реализация по умолчанию.
  • Обработчик: отрицать политики пула потоков.

Cheng политика бассейн отказ

RejectedExecutionHandler конструктор последнего параметра для отклонения политики определяет пул потоков. Когда пришел запрос текущей задачи, а система в это время и ручки, однако, мы должны принять соответствующие стратегии является отказ в обслуживании.

По умолчанию существует четыре типа:

  • AbortPolicy Стратегия: Стратегия будет направлять бросок, предохраняющая систему работать должным образом.
  • Стратегия CallerRunsPolicy: до тех пор, как пул потоков не закрыт, политика непосредственно в потоке вызывающего, бегите текущая задача отбрасывается.
  • Стратегия DiscardOleddestPolicy: Эта стратегия будет отбрасывать старейший один запрос, то есть задача должна быть выполнена, и попытаться вновь представить текущую работу.
  • DiscardPolicy стратегии: стратегия отбрасывать задачи не могут быть обработаны без какого-либо лечения.

Конечно, в дополнение к по умолчанию четыре вида стратегий могут также подгоняет отклонять политику, основанную на потребностях бизнеса. Реализуя интерфейс RejectedExecutionHandler при создании объекта в качестве параметра ThreadPoolExecutor.

В весенне-интегрально-ядро будет настроено CallerBlocksPolicy, соответствующий код выглядит следующим образом:

public class CallerBlocksPolicy implements RejectedExecutionHandler {
    private static final Log logger = LogFactory.getLog(CallerBlocksPolicy.class);
    private final long maxWait;

    public CallerBlocksPolicy(long maxWait) {
        this.maxWait = maxWait;
    }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (!executor.isShutdown()) {
            try {
                BlockingQueue<Runnable> queue = executor.getQueue();
                if (logger.isDebugEnabled()) {
                    logger.debug("Attempting to queue task execution for " + this.maxWait + " milliseconds");
                }

                if (!queue.offer(r, this.maxWait, TimeUnit.MILLISECONDS)) {
                    throw new RejectedExecutionException("Max wait time expired to queue task");
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Task execution queued");
                    }

                }
            } catch (InterruptedException var4) {
                Thread.currentThread().interrupt();
                throw new RejectedExecutionException("Interrupted", var4);
            }
        } else {
            throw new RejectedExecutionException("Executor has been shut down");
        }
    }
}复制代码

Выполнение пула потоков

После создания ThreadPoolExecutor, когда вы передаете задачу в пул потоков, как правило, используют метод выполнения. Выполнить метод блок-схема алгоритма выполняется следующим образом:

образ

  • Если число потоков ядра в выживании пула потоков меньше, чем число нитей corePoolSize, создаст основной поток пула потоков для обработки задачи, представленный.
  • Если базовое число потоков в пуле потоков заполнено, что равно числу нитей была corePoolSize, новая задача представляется, он будет поставлен в очереди задачу workQueue в очереди на выполнение.
  • Когда число потоков в пуле потоков, которые пережило corePoolSize равны, и задача очереди workQueue была полна, количество потоков, чтобы определить, является ли maximumPoolSize, независимо от того, что максимальное количество потоков полон, если не достичь, создать поток для выполнения непрофильных задач, представленных.
  • Если текущее число потоков достигло maximumPoolSize, а также новые задачи приходят, а затем отказался направлять использование обработки политики.

анализ исходного кода

Следующий взгляд на JDK8 в ThreadPoolExecutor выполнить метод в исходном коде для достижения:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    // 线程池本身的状态跟worker数量使用同一个变量ctl来维护
    int c = ctl.get();
    // 通过位运算得出当然线程池中的worker数量与构造参数corePoolSize进行比较
    if (workerCountOf(c) < corePoolSize) {
        // 如果小于corePoolSize,则直接新增一个worker,并把当然用户提交的任务command作为参数,如果成功则返回。
        if (addWorker(command, true))
            return;
        // 如果失败,则获取最新的线程池数据
        c = ctl.get();
    }
    // 如果线程池仍在运行,则把任务放到阻塞队列中等待执行。
    if (isRunning(c) && workQueue.offer(command)) {
        // 这里的recheck思路是为了处理并发问题
        int recheck = ctl.get();
        // 当任务成功放入队列时,如果recheck发现线程池已经不再运行了则从队列中把任务删除
        if (! isRunning(recheck) && remove(command))
            //删除成功以后,会调用构造参数传入的拒绝策略。
            reject(command);
         // 如果worker的数量为0(此时队列中可能有任务没有执行),则新建一个worker(由于此时新建woker的目的是执行队列中堆积的任务,
         // 因此入参没有执行任务,详细逻辑后面会详细分析addWorker方法)。
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果前面的新增woker,放入队列都失败,则会继续新增worker,此时线程池的状态是woker数量达到corePoolSize,阻塞队列任务已满
    // 只能基于maximumPoolSize参数新建woker
    else if (!addWorker(command, false))
        // 如果基于maximumPoolSize新建woker失败,此时是线程池中线程数已达到上限,队列已满,则调用构造参数中传入的拒绝策略
        reject(command);
}复制代码

Посмотрите addWorker следующий вызов метода в коде выше и синтаксического анализа исходного кода для достижения:

private boolean addWorker(Runnable firstTask, boolean core) {
    // 这里有一段基于CAS+死循环实现的关于线程池状态,线程数量的校验与更新逻辑就先忽略了,重点看主流程。
    //...

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
         // 把指定任务作为参数新建一个worker线程
        w = new Worker(firstTask);
        // 这里是重点w.thread是通过线程池构造函数参数threadFactory生成的woker对象
        // 也就是说这个变量t就是代表woker线程。绝对不是用户提交的线程任务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()) 
                        throw new IllegalThreadStateException();
                    // 把新建的woker线程放入集合保存,这里使用的是HashSet
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 然后启动woker线程
                 // 该变量t代表woker线程,会调用woker的run方法
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            // 如果woker启动失败,则进行一些善后工作,比如说修改当前woker数量等
            addWorkerFailed(w);
    }
    return workerStarted;
}复制代码

Метод addWorker в основном сделать, это создать новую нить Woker, Уокер добавлен в коллекцию. В приведенных выше вызовов метода к методу выполнения класса Worker, и в конечном счете, выполненный методом runWorker.

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

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    // task就是Woker构造函数入参指定的任务,即用户提交的任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
        //一般情况下,task都不会为空(特殊情况上面注释中也说明了),因此会直接进入循环体中
        //这里getTask方法是要重点说明的,它的实现跟我们构造参数设置存活时间有关
        //我们都知道构造参数设置的时间代表了线程池中的线程,即woker线程的存活时间,如果到期则回收woker线程,这个逻辑的实现就在getTask中。
        //来不及执行的任务,线程池会放入一个阻塞队列,getTask方法就是去阻塞队列中取任务,用户设置的存活时间,就是
        //从这个阻塞队列中取任务等待的最大时间,如果getTask返回null,意思就是woker等待了指定时间仍然没有
        //取到任务,此时就会跳过循环体,进入woker线程的销毁逻辑。
        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.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 {
                //这里设为null,也就是循环体再执行的时候会调用getTask方法
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //当指定任务执行完成,阻塞队列中也取不到可执行任务时,会进入这里,做一些善后工作,比如在corePoolSize跟maximumPoolSize之间的woker会进行回收
        processWorkerExit(w, completedAbruptly);
    }
}复制代码

Задача процесс выполнения Woker нить назначаются сначала выполнить инициализацию, будет пытаться получить исполняемую задачу блокировать очереди после выполнения завершения, если до сих пор ни одна из задач не могут быть выполнены в течение указанного времени, а затем ввести разрушение логики. Вот только выздоровел corePoolSize и maximumPoolSize направить эту часть Уокера.

Выполнить и представить разницу

Кроме того, вы можете использовать для выполнения задач, выполнить метод может также использовать метод представить. Основное различие: EXECUTE не должно применяться к возвращаемому значению сцены интереса, представить метод применит к необходимости сосредоточиться на возвращаемом значении сцены.

Обработка исключений

При выполнении заданий происходит, исключение, то как с этим бороться? Первый взгляд на то, как иметь дело с тем, когда кроме резьбы резьбы.

В задаче по Try ... Catch исключение захватывается и обрабатывается, следующий код:

Thread t = new Thread(() -> {
    try {
        System.out.println(1 / 0);
    } catch (Exception e) {
        LOGGER.error(e.getMessage(), e);
    }
});
t.start();复制代码

Если несколько потоков механизм обработки исключений задача по умолчанию является то же самое, вы можете установить резьбы по умолчанию механизм обработки исключений через UncaughtExceptionHandler класса Thread.

UncaughtExceptionHandler реализовывать интерфейсы и вызовы Thread метод # setUncaughtExceptionHandler (UncaughtExceptionHandler). Если вы хотите установить глобальный механизм обработки исключений по умолчанию, вы можете вызвать метод Thread # setDefaultUncaughtExceptionHandler (UncaughtExceptionHandler).

ThreadGroup обеспечивает механизм обработки исключений по умолчанию выглядит следующим образом:

public void uncaughtException(Thread t, Throwable e) {
    if (parent != null) {
        parent.uncaughtException(t, e);
    } else {
        Thread.UncaughtExceptionHandler ueh =
            Thread.getDefaultUncaughtExceptionHandler();
        if (ueh != null) {
            ueh.uncaughtException(t, e);
        } else if (!(e instanceof ThreadDeath)) {
            System.err.print("Exception in thread \""
                             + t.getName() + "\" ");
            e.printStackTrace(System.err);
        }
    }
}复制代码

ThreadPoolExecutor обработка исключений механизм Нить то же самое. В то же время, ThreadPoolExecutor предусмотрен способ настройки обработки исключений uncaughtExceptionHandler. Следующий пример:

public class ThreadPool {

    public static void main(String[] args) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("demo-pool-%d")
                .setUncaughtExceptionHandler(new LogUncaughtExceptionHandler())
                .build();

        ExecutorService pool = new ThreadPoolExecutor(5, 200,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

        pool.execute(() -> {
            throw new RuntimeException("测试异常");
        });

        pool.shutdown();
    }

    static class  LogUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("打印LogUncaughtExceptionHandler中获得的异常信息:" + e.getMessage());
        }
    }
}复制代码

Но следует отметить, что применение метода UncaughtExceptionHandler применяется только для выполнения способа выполнения задачи, и метод должен представить недействительны. Отправить выполнение задачи может быть получено брошенным исключение получить возвращается метод объекта будущего, а затем обрабатываются. Этот метод можно считать одним из отличий выполнить и представить метод.

Очередь бассейн Общий поток

Работа очередь пула потоков имеет следующее:

  • ArrayBlockingQueue: ограниченная очередь, использовать массив для достижения ограниченной блокирующей очереди, а количество заказов FIFO.
  • LinkedBlockingQueue: емкость очереди может быть установлен, на основе блокировки структуры очереди списка, задача сортировки FIFO, емкость может быть установлен, чтобы выбрать, если не установлен, то очередь неблокирующая граница, длина максимум Integer.MAX_VALUE, более высокая пропускная способность, как правило, в ArrayBlockingQuene; newFixedThreadPool использует эту очередь пула потоков.
  • DelayQueue: задержка очередь, очередь является временным выполнением задачи периода задержки. Согласно указанному времени от малых до больших исполнения заказа, или имеет заказ очереди в соответствии с вкладышем. newScheduledThreadPool использует эту очередь пула потоков.
  • PriorityBlockingQueue: очереди приоритетов неограничено очередь блокировки с приоритетом.
  • SynchronousQueue: синхронная очередь не хранит блокирующие элементы очереди, каждая операция вставки должна ждать, пока другой поток не вызывает операцию удаления или операцию вставки в заблокированном состоянии, пропускная способность, как правило, выше, чем LinkedBlockingQuene, newCachedThreadPool использует эту очередь пула потоков ,

Закройте пул потоков

Закрыть пул потоков можно назвать shutdownNow и два способа отключения достичь.

shutdownNow: на задачах выполняются все выданные прерывания (), выполнение остановки задания еще не началось выполнение отмены всех, а не вернуться к списку задач, чтобы начать.

Выключение: Когда мы называем выключение, пул потоков больше не будет принимать новые задачи, но не будет вынужден прекратить было подано или задание выполняется.

Ссылка в статье:

https://www.jianshu.com/p/5df6e38e4362

https://juejin.im/post/5d1882b1f265da1ba84aa676

Оригинальная ссылка: « вопросы интервью - статья о Java пула потоков достаточно


Программа New Horizons : возбуждающий и рост не будет хватать

Программа New Horizons - микро-номер канала общественного

рекомендация

отjuejin.im/post/5df7864ce51d455821125b07
рекомендация