Философия Java

         

Когда процессов слишком много


В некоторый момент вы увидите, что ColorBoxes

совсем увяз в выполнении. На моей машине это возникало примерно после таблицы 10х10. Но почему такое происходит? Вы подозрительны если считаете, что возможно Swing может что-то творить с этим, так что, вот пример, который проверяет данное утверждение путем создания небольшого количества процессов. Следующий исходный код переделан так, чтобы ArrayList реализует Runnable

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

//: c14:ColorBoxes2.java

// Balancing thread use.

// <applet code=ColorBoxes2 width=600 height=500>

// <param name=grid value="12">

// <param name=pause value="50">

// </applet>

import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import com.bruceeckel.swing.*;

class CBox2 extends JPanel { private static final Color[] colors = { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow }; private Color cColor = newColor(); private static final Color newColor() { return colors[ (int)(Math.random() * colors.length) ]; } void nextColor() { cColor = newColor(); repaint(); } public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(cColor); Dimension s = getSize(); g.fillRect(0, 0, s.width, s.height); } }

class CBoxList extends ArrayList implements Runnable { private Thread t; private int pause; public CBoxList(int pause) { this.pause = pause; t = new Thread(this); } public void go() { t.start(); } public void run() { while(true) { int i = (int)(Math.random() * size()); ((CBox2)get(i)).nextColor(); try { t.sleep(pause); } catch(InterruptedException e) { System.err.println("Interrupted"); } } } public Object last() { return get(size() - 1);} }


public class ColorBoxes2 extends JApplet { private boolean isApplet = true; private int grid = 12; // Shorter default pause than ColorBoxes:

private int pause = 50; private CBoxList[] v; public void init() { // Get parameters from Web page:

if (isApplet) { String gsize = getParameter("grid"); if(gsize != null) grid = Integer.parseInt(gsize); String pse = getParameter("pause"); if(pse != null) pause = Integer.parseInt(pse); } Container cp = getContentPane(); cp.setLayout(new GridLayout(grid, grid)); v = new CBoxList[grid]; for(int i = 0; i < grid; i++) v[i] = new CBoxList(pause); for (int i = 0; i < grid * grid; i++) { v[i % grid].add(new CBox2()); cp.add((CBox2)v[i % grid].last()); } for(int i = 0; i < grid; i++) v[i].go(); } public static void main(String[] args) { ColorBoxes2 applet = new ColorBoxes2(); applet.isApplet = false; if(args.length > 0) applet.grid = Integer.parseInt(args[0]); if(args.length > 1) applet.pause = Integer.parseInt(args[1]); Console.run(applet, 500, 400); } } ///:~

В ColorBoxes2 создается массив CBoxList и инициализируется для хранения grid CBoxList, каждый из которых знает на сколько долго необходимо засыпать. Затем в каждый CBoxList добавляется аналогичное количество объектов CBox2 и  каждый список вызывает go(), что запускает процесс.

CBox2 аналогичен CBox: он рисует себя произвольно выбранным цветом. Но это и все что CBox2 делает. Вся работа с процессами теперь перемещена в CBoxList.

CBoxList также может иметь унаследованный Thread и иметь объект член типа ArrayList. Данное решение имеет преимущества в том, что методам add() и get() затем может быть передан особый аргумент и возвращаемое значение, вместо общих Object'ов. (И их имя также может быть изменено на что-то более кортокое.) На первый взгляд кажется, что приведенном здесь пример требует меньше кодирования. Дополнительно, он автоматически сохраняет все функции выполняемые ArrayList.  Со всеми приведениями и скобками, необходимыми для get() это не будет выходом при росте основного кода программы.



Как и прежде, при реализации Runnable вы не получаете все, что предоставляется вместе с Thread, так что вам необходимо создать новый Thread и самим определить его конструктор, чтобы иметь что-либо для start(), так, как это было сделано в конструкторе CBosList и в go().  Метод run() просто выбирает случайны номер элемента в листе и вызывает nextCollor() для этого элемента, чтобы он применил новый, солучайно выбранный цвет.



Во время работы этой программы можно видеть, что она действительно выполняется быстрее и быстрее реагирует на действия (например, при попытке завершения все происходит намного быстрее), и похоже, она не затормаживается при бОльших размерах таблицы. Таким образом, в уравнение процессов был добавлен новый фактор: вы должны быть наблюдательны, чтобы заметить, что у вас не "слишком много процессов" (чтобы это не означало в вашем конкретной программе или платформе, как например в нашем случае, ColorBoxes имеет только один процесс, который ответственен за всю перерисовку, что приводит к снижению производительности из-за слишком большого количества запросов). Если у вас слишком много процессов, то можно попробовать использовать одну из техник приведенных выше для "балансировки" количества процессов в вашей программе. Если наблюдается проблема с производительностью в программе с множеством процессов, то необходимо проверить следующее:

 Достаточно ли у вас вызовов sleep( ), yield( ), и/или wait( )?

 Насколько продолжителен вызов sleep( )?

 Запустили ли вы слишком много процессов?

 Пробовали ли вы различные платформы и JVM?

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


Содержание раздела