Что такое pool java
Перейти к содержимому

Что такое pool java

  • автор:

Главная

Пул строк — это конкретная реализация JVM концепции string interning.

Когда мы используем двойные кавычки для создания строки, сначала ищется строка в пуле с таким же значением, если находится, то просто возвращается ссылка, иначе создается новая строка в пуле, а затем возвращается ссылка .
Пул строк возможен исключительно благодаря неизменяемости строк в Java и реализации идеи интернирования строк. Пул строк также является примером паттерна Приспособленец (Flyweight).
когда мы используем оператор new , мы принуждаем класс String создать новый объект строки, а затем мы можем использовать метод intern() для того, чтобы поместить строку в пул, или получить из пула ссылку на другой объект String с таким же значением.

Когда метод intern() вызван, если пул строк уже содержит строку, эквивалентную к нашему объекту, что подтверждается методом equals (Object), тогда возвращается ссылка на строку из пула. В противном случае объект строки добавляется в пул и ссылка на этот объект возвращается.

Сборка мусора :

До Java 7 , пул Java String помещался в пространство PermGen , которое имеет фиксированный размер — его нельзя развернуть во время выполнения и не подходит для сборки мусора .
Риск интернирования строк в PermGen (вместо кучи ) заключается в том, что мы можем получить ошибку OutOfMemory от JVM, если интернируем слишком много строк .

Начиная с Java 7, пул Java String хранится в пространстве Heap , собираемым JVM . Преимущество этого подхода заключается в уменьшении риска ошибки OutOfMemory, поскольку строки, на которые нет ссылок, будут удалены из пула, тем самым освобождая память.

работа пула строк :
 public class MyStringPool < public static void main(String[] args) < String s1 = "Cat"; // создаем строку и помещаем ее в pool строк String s2 = "Cat"; // создаем строку и помещаем ее в pool строк String s3 = new String("Cat"); // создаем новый обьект (не помещаем его в pool) System.out.println("s1 == s2 :"+(s1==s2)); System.out.println("s1 == s3 :"+(s1==s3)); > > s1 == s2 :true s1 == s3 :false 
*.intern() :
 String str1 = "first"; String str2 = "first"; String str3 = new String("first"); str3 = str3.intern(); System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str3); true true true 
Полезные ссылки:
  • «str».intern() in JavaDoc (Java 8)
  • «str».intern() in JavaDoc (Java 7)
  • «str».intern() in JavaDoc (Java 6)

Пул объектов (Object pool)

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

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

//: :PoolManager.java
package singleton;

public class PoolManager private static class PoolItem boolean inUse = false ;
Object item;

PoolItem ( Object item ) this .item = item;
>
>

private ArrayList items = new ArrayList () ;

public void add ( Object item ) items.add ( new PoolItem ( item )) ;
>

static class EmptyPoolException extends Exception >

public Object get () throws EmptyPoolException for ( int i = 0 ; i < items.size () ; i++ ) PoolItem pitem = ( PoolItem ) items.get ( i ) ;
if ( pitem.inUse == false ) pitem.inUse = true ;
return pitem.item;
>
>
// :
throw new EmptyPoolException () ;
// return null; //
>

public void release ( Object item ) for ( int i = 0 ; i < items.size () ; i++ ) PoolItem pitem = ( PoolItem ) items.get ( i ) ;
if ( item == pitem.item ) pitem.inUse = false ;
return ;
>
>
throw new RuntimeException ( item + » not found» ) ;
>
> // /:~

//: singleton:ConnectionPoolDemo.java
package singleton;

interface Connection Object get () ;

void set ( Object x ) ;
>

class ConnectionImplementation implements Connection public Object get () return null ;
>

public void set ( Object s ) >
>

class ConnectionPool < // Синглетон
private static PoolManager pool = new PoolManager () ;

public static void addConnections ( int number ) for ( int i = 0 ; i < number; i++ )
pool.add ( new ConnectionImplementation ()) ;
>

public static Connection getConnection ()
throws PoolManager.EmptyPoolException return ( Connection ) pool.get () ;
>

public static void releaseConnection ( Connection c ) pool.release ( c ) ;
>
>

public class ConnectionPoolDemo extends TestCase static ConnectionPool.addConnections ( 5 ) ;
>

public void test () Connection c = null ;
try c = ConnectionPool.getConnection () ;
>
catch ( PoolManager.EmptyPoolException e ) throw new RuntimeException ( e ) ;
>
c.set ( new Object ()) ;
c.get () ;
ConnectionPool.releaseConnection ( c ) ;
>

public void test2 () Connection c = null ;
try c = ConnectionPool.getConnection () ;
>
catch ( PoolManager.EmptyPoolException e ) throw new RuntimeException ( e ) ;
>
c.set ( new Object ()) ;
c.get () ;
ConnectionPool.releaseConnection ( c ) ;
>

public static void main ( String args []) junit.textui.TestRunner.run ( ConnectionPoolDemo. class ) ;
>
> // /:~

Упражнения

  1. Добавьте тестирование модуля ConnectionPoolDemo.java, чтобы продемонстрировать проблему, когда клиент может не освободить соединение, а продолжать использовать его.

Как это работает в мире java. Пул потоков

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

Представим, что у вас которая вызывает большую загрузку процессора:

public class Counter < public Double count(double a) < for (int i = 0; i < 1000000; i++) < a = a + Math.tan(a); >return a; > >

Мы хотим как можно быстрее обработать ряд таких задач, попробуем*:

public class SingleThreadClient < public static void main(String[] args) < Counter counter = new Counter(); long start = System.nanoTime(); double value = 0; for (int i = 0; i < 400; i++) < value += counter.count(i); >System.out.println(format("Executed by %d s, value : %f", (System.nanoTime() - start) / (1000_000_000), value)); > >

На моей тачке с 4 физическими ядрами использование ресурсов процессора top -pid :

image

Время выполнения 104 сек.

Как вы заметили, загрузка одного процессора на один java-процесс с одним выполняемым потоком составляет 100%, но общая загрузка процессора в пользовательском пространстве составляет всего 2,5%, и у нас есть много неиспользуемых системных ресурсов.

Давайте попробуем использовать больше, добавив больше рабочих потоков:

public class MultithreadClient < public static void main(String[] args) throws ExecutionException, InterruptedException < ExecutorService threadPool = Executors.newFixedThreadPool(8); Counter counter = new Counter(); long start = System.nanoTime(); List> futures = new ArrayList<>(); for (int i = 0; i < 400; i++) < final int j = i; futures.add( CompletableFuture.supplyAsync( () ->counter.count(j), threadPool )); > double value = 0; for (Future future : futures) < value += future.get(); >System.out.println(format("Executed by %d s, value : %f", (System.nanoTime() - start) / (1000_000_000), value)); threadPool.shutdown(); > >

image

ThreadPoolExecutor

Для ускорения мы использовали ThreadPool — в java его роль играет ThreadPoolExecutor, который может быть реализован непосредственно или из одного из методов в классе Utilities. Если мы заглянем внутрь ThreadPoolExecutor, мы можем найти очередь:

private final BlockingQueue workQueue;

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

public void execute(Runnable command) < . if (workerCountOf(c) < corePoolSize) < if (addWorker(command, true)) return; . if (isRunning(c) && workQueue.offer(command)) < . addWorker(null, false); . >>

Каждый addWorker запускает новый поток с задачей Runnable, которая опрашивает workQueue на наличие новых задач и выполняет их.

final void runWorker(Worker w)

ThreadPoolExecutor имеет очень понятный javadoc, поэтому нет смысла его перефразировать. Вместо этого, давайте попробуем сделать наш собственный:

public class ThreadPool implements Executor < private final QueueworkQueue = new ConcurrentLinkedQueue<>(); private volatile boolean isRunning = true; public ThreadPool(int nThreads) < for (int i = 0; i < nThreads; i++) < new Thread(new TaskWorker()).start(); >> @Override public void execute(Runnable command) < if (isRunning) < workQueue.offer(command); >> public void shutdown() < isRunning = false; >private final class TaskWorker implements Runnable < @Override public void run() < while (isRunning) < Runnable nextTask = workQueue.poll(); if (nextTask != null) < nextTask.run(); >> > > >

Теперь давайте выполним ту же задачу, что и выше, с нашим пулом.
Меняем строку в MultithreadClient:

// ExecutorService threadPool = Executors.newFixedThreadPool (8); ThreadPool threadPool = new ThreadPool (8);

Время выполнения практически одинаковое — 15 секунд.

Размер пула потоков

Попробуем еще больше увеличить количество запущенных потоков в пуле — до 100.

ThreadPool threadPool = new ThreadPool(100);

Мы можем видеть, что время выполнения увеличилось до 28 секунд — почему это произошло?

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

Количество переключений контекста процесса можно увидеть, посмотрев на csw параметр при выводе команды top.

image

На 8 потоках:

image

На 100 потоках:

Как выбрать размер пула?

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

Предполагая, что потоки не блокируют друг друга, нет циклов ожидания I/O, и время обработки задач одинаково, оптимальный пул потоков = Runtime.getRuntime().availableProcessors() + 1.

Если потоки в основном ожидают I/O, то оптимальный размер пула должен быть увеличен на отношение между временем ожидания процесса и временем вычисления. Например. У нас есть процесс, который тратит 50% времени в iowait, тогда размер пула может быть 2 * Runtime.getRuntime().availableProcessors() + 1.

Другие виды пулов

  1. Пул потоков с ограничением по памяти, который блокирует отправку задачи, когда в очереди слишком много задач MemoryAwareThreadPoolExecutor
  2. Пул потоков, который регистрирует JMX-компонент для контроля и настройки размера пула в runtime.
    JMXEnabledThreadPoolExecutor

Исходный код можно найти здесь.

Что такое pool java

Наверняка вам известно, что при сравнении двух объектов в Java на == обычно получается не тот результат, что нужен, т.к. идет сравнение ссылок на объекты, а не данных этих объектов.

2015-02-19T10:02 19 фев 2015 10:02
Просмотров: 11541

Наверняка вам известно, что при сравнении двух объектов в Java на == обычно получается не тот результат, что нужен, т.к. идет сравнение ссылок на объекты, а не данных этих объектов.

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

public class Temp < public static void main(String []args)< Integer i1 = 10; Integer i2 = 10; System.out.println(i1 == i2); >>

Как думаете, что он напечатает? А напечатает он true.

А вот еще один код:

public class Temp < public static void main(String []args)< Integer i1 = 130; Integer i2 = 130; System.out.println(i1 == i2); >>

Этот код уже выведет false.

На первый взгляд странно, не так ли? Сейчас объясню.

В Java есть пул(pool) целых чисел в промежутке [-128;127]. Т.е. если мы создаем Integer в этом промежутке, то вместо того, чтобы каждый раз создавать новый объект, JVM берет их из пула. Таким образом, в первом примере i1 и i2 указывают на один и тот же объект из пула, а во втором — создаются два разных объекта. Отсюда и результат. Стоит заметить, что в приведенных выше примерах не использовалось ключевое слово new.

public class Temp < public static void main(String []args)< Integer i1 = new Integer(10); Integer i2 = new Integer(10); System.out.println(i1 == i2); >>

Результатом будет false. Тут мы уже явно говорим, что хотим создать новый(new) объект, так что ссылки разные.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *