Объясните роль пула потоков и способы использования пула потоков в Java.

Серверные приложения (например, базы данных и веб-серверы) должны обрабатывать высокопараллельные и непродолжительные запросы от клиентов, поэтому частое создание потоков, необходимых для обработки этих запросов, является очень ресурсоемкой операцией. Обычный метод - создать новый поток для нового запроса.Хотя этот метод кажется простым в реализации, он имеет серьезные недостатки. Создание нового потока для каждого запроса потребует больше времени и системных ресурсов при создании и удалении потоков. Следовательно, JVM, которая создает слишком много потоков одновременно, может привести к нехватке системной памяти.Это требует ограничения количества создаваемых потоков, то есть необходимо использовать пул потоков.

1. Что такое пул потоков в Java?

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

  • Java предоставляет среду исполнителя, основанную на интерфейсе Executor и его подчиненных интерфейсах ExecutorService и ThreadPoolExecutor . Используя Executor, вам нужно только реализовать интерфейс Runnable, чтобы завершить задачу потока и передать ее исполнителю для выполнения.
  • Инкапсулируйте пул потоков для вас и сосредоточьтесь на задачах программирования на реализации конкретных задач, а не на механизме реализации потоков.
  • Чтобы использовать пул потоков, мы сначала создаем объект ExecutorService, а затем передаем ему набор задач. Класс ThreadPoolExcutor может установить инициализацию пула потоков и максимальную емкость потока.

На приведенном выше рисунке показано, что инициализация пула потоков имеет 3 потока и 5 объектов задач, которые должны быть запущены в очереди задач.

Метод пула потоков исполнителя | Метод | Описание | | --- | --- | | newFixedThreadPool (int) | Создать пул потоков с фиксированным количеством потоков, параметр int указывает количество потоков в пуле потоков | | newCachedThreadPool () | Создать кэшируемый пул потоков, который может гибко восстанавливать простаивающие потоки. Если нет незанятого потока, создайте новый поток для обработки задачи. | | newSingleThreadExecutor () | Создать однопоточный пул потоков, он будет использовать только один рабочий поток для выполнения задач | | newScheduledThreadPool | Создать пул потоков фиксированной длины, поддерживать синхронизацию и периодическое выполнение задач |

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

Два, пример пула потоков

В следующем материале мы представим исполнителя пула потоков.

Действия по созданию пула потоков для обработки задач

  1. Создайте объект задачи (реализуйте интерфейс Runnable) для выполнения определенной логики задачи
  2. Используйте Executors для создания пула потоков ExecutorService
  3. Передать объект задачи на выполнение ExecutorService для обработки задачи
  4. Остановить пул потоков Executor
//第一步: 创建一个任务对象(实现Runnable接口),用于执行具体的任务逻辑 (Step 1) 
class Task implements Runnable  {
    private String name;

    public Task(String s) {
        name = s;
    }

    // 打印任务名称并Sleep 1秒
    // 整个处理流程执行5次
    public void run() {
        try{
            for (int i = 0; i<=5; i++) {
                if (i==0) {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("任务初始化" + name +" = " + ft.format(d));
                    //第一次执行的时候,打印每一个任务的名称及初始化的时间
                }
                else{
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("任务正在执行" + name +" = " + ft.format(d));
                    // 打印每一个任务处理的执行时间
                }
                Thread.sleep(1000);
            }
            System.out.println("任务执行完成" + name);
        }  catch(InterruptedException e)  {
            e.printStackTrace();
        }
    }
}

Прецедент

public class ThreadPoolTest {
    // 线程池里面最大线程数量
    static final int MAX_SIZE = 3;

    public static void main (String[] args) {
        // 创建5个任务
        Runnable r1 = new Task("task 1");
        Runnable r2 = new Task("task 2");
        Runnable r3 = new Task("task 3");
        Runnable r4 = new Task("task 4");
        Runnable r5 = new Task("task 5");

        // 第二步:创建一个固定线程数量的线程池,线程数为MAX_SIZE
        ExecutorService pool = Executors.newFixedThreadPool(MAX_SIZE);

        // 第三步:将待执行的任务对象交给ExecutorService进行任务处理
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        pool.execute(r4);
        pool.execute(r5);

        // 第四步:关闭线程池
        pool.shutdown();
    }
} 

Пример результата выполнения

任务初始化task 1 = 05:25:55
任务初始化task 2 = 05:25:55
任务初始化task 3 = 05:25:55
任务正在执行task 3 = 05:25:56
任务正在执行task 1 = 05:25:56
任务正在执行task 2 = 05:25:56
任务正在执行task 1 = 05:25:57
任务正在执行task 3 = 05:25:57
任务正在执行task 2 = 05:25:57
任务正在执行task 3 = 05:25:58
任务正在执行task 1 = 05:25:58
任务正在执行task 2 = 05:25:58
任务正在执行task 2 = 05:25:59
任务正在执行task 3 = 05:25:59
任务正在执行task 1 = 05:25:59
任务正在执行task 1 = 05:26:00
任务正在执行task 2 = 05:26:00
任务正在执行task 3 = 05:26:00
任务执行完成task 3
任务执行完成task 2
任务执行完成task 1
任务初始化task 5 = 05:26:01
任务初始化task 4 = 05:26:01
任务正在执行task 4 = 05:26:02
任务正在执行task 5 = 05:26:02
任务正在执行task 4 = 05:26:03
任务正在执行task 5 = 05:26:03
任务正在执行task 5 = 05:26:04
任务正在执行task 4 = 05:26:04
任务正在执行task 4 = 05:26:05
任务正在执行task 5 = 05:26:05
任务正在执行task 4 = 05:26:06
任务正在执行task 5 = 05:26:06
任务执行完成task 4
任务执行完成task 5

Как показано в результате выполнения программы, задача 4 или задача 5 выполняется только тогда, когда поток в пуле становится неактивным. Перед этим в очередь на выполнение будут помещены дополнительные задачи.

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

Одним из основных преимуществ использования этого метода пула потоков является то, что если вы хотите обрабатывать 10 000 запросов за раз, но не хотите создавать 10 000 потоков, чтобы избежать простоев, вызванных чрезмерным использованием системных ресурсов. Вы можете использовать этот метод для создания пула потоков, содержащего 500 потоков, и вы можете отправить 500 запросов в пул потоков. ThreadPool будет создавать до 500 потоков в это время, обрабатывая 500 запросов за раз. После завершения процесса любого потока ThreadPool внутренне выделит 501-й запрос этому потоку и продолжит выполнение той же операции для всех оставшихся запросов. В случае относительно ограниченных системных ресурсов пул потоков является эффективным решением для обеспечения стабильной работы программы.

В-третьих, использование пула потоков и настройка

  1. Взаимоблокировка: хотя взаимоблокировка может возникнуть в любой многопоточной программе, пул потоков представляет другой случай взаимоблокировки, когда все потоки выполнения ожидают результата выполнения заблокированного потока в очереди, в результате чего поток не может продолжить выполнение.
  2. Утечка потока : если поток в пуле потоков не возвращается правильно, когда задача завершена, произойдет утечка потока. Например, если поток генерирует исключение, а класс пула не перехватывает это исключение, поток завершится аварийно, и размер пула потоков будет уменьшен на единицу. Если эта ситуация повторяется несколько раз, пул потоков в конечном итоге станет пустым, и никакие потоки не смогут использоваться для выполнения других задач.
  3. Частая ротация потоков: если размер пула потоков очень велик, переключение контекста между потоками будет тратить много времени. Так что в случае наличия системных ресурсов дело не в том, что чем больше пул потоков, тем лучше.

Оптимизация размера пула потоков. Оптимальный размер пула потоков зависит от количества доступных процессоров и характера обрабатываемых задач. Для задач, интенсивно использующих ЦП, если в системе имеется N логических процессорных ядер, максимальный размер пула потоков N или N + 1 позволит достичь максимальной эффективности. Для задач с интенсивным вводом-выводом необходимо учитывать отношение времени ожидания (W) запроса ко времени обработки службы (S). Максимальный размер пула потоков составляет N * (1+ W / S) для достижения максимальной эффективности.

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

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

  • Данная статья воспроизводится с указанием источника (соединение должно быть приложено, а текст не может быть воспроизведен только): Блог Letter Brother .

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

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

отblog.csdn.net/hanxiaotongtong/article/details/112598976