Повторное изучение Runnable
Ранее в этой главе я советовал, чтобы вы тщательно подумали прежде чем сделать апплет или основной Frame реализацией отRunnable. Конечно, если вы должны наследовать от класса и хотите добавить поведение как у процесса для класса, то Runnable будет правильным решением. Последний пример в этой главе показывает это создав класс RunnableJPanel
рисующий различные цвета. Данное приложение сделано так, что принимает различные значения из командной строки чтобы определить размер таблицы цветов и какой промежуток времени sleep() между перерисовкой другим цветом. Играясь с этими параметрами можно обнаружить некоторое интересное и, возможно, необъяснимое поведение процесса:
//: c14:ColorBoxes.java
// Using the Runnable interface.
// <applet code=ColorBoxes width=500 height=400>
// <param name=grid value="12">
// <param name=pause value="50">
// </applet>
import javax.swing.*; import java.awt.*; import java.awt.event.*; import com.bruceeckel.swing.*;
class CBox extends JPanel implements Runnable { private Thread t; private int pause; 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) ]; } public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(cColor); Dimension s = getSize(); g.fillRect(0, 0, s.width, s.height); } public CBox(int pause) { this.pause = pause; t = new Thread(this); t.start(); } public void run() { while(true) { cColor = newColor(); repaint(); try { t.sleep(pause); } catch(InterruptedException e) { System.err.println("Interrupted"); } } } }
public class ColorBoxes extends JApplet { private boolean isApplet = true; private int grid = 12; private int pause = 50; 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)); for (int i = 0; i < grid * grid; i++) cp.add(new CBox(pause)); } public static void main(String[] args) { ColorBoxes applet = new ColorBoxes(); 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); } } ///:~
ColorBoxes обычный апплет/приложение с init( ) реализующим GUI. Это устанавливает GridLayout так, что он имеет ячейки таблицы (grid) в каждом направлении. Затем, для заполнения таблцы, добавляется соответствующее количество объектов CBox передав значение переменной pause для каждой из них. В методе main() можно видеть как pause и grid имеют значения по умолчанию, которые можно изменить передав их через параметры командной строки, либо изменив параметры апплета.
Вся работа происходит в CBox, который является наследником от JPanel и реализует интерфейс Runnable так, что каждый JPanel может быть также и Thread. Запомните, что когда вы реализуете Runnable вы не создаете объекта Thread, а просто класс, имеющий метод run(). Таким образом, можно явно создать объект Thread и применить объект Runnable в конструкторе, затем вызвать start() (что происходит в конструкторе). В CBox данный процесс называется t.
Обратите внимание на массив color являющейся списком всех цветовых значений в классе Color. Это используется в NewColor для случайного выбора цвета. Текущее значение цвета для ячейки определяется как cColor.
paintComponent() совершенно просто - устанавливается значение цвета для cColor и весь JPanel закрашивается данным цветом.
Бесконечный цикл в run() устанавливает cColor в новое, случайно выбранное значение, а затем вызывает метод repaint() для отображения. Затем процесс sleep() (засыпает) на какое-то время, определенное в командной строке.
Именно из-за того, что данный пример гибок, и каждый элемент JPanel действует как процесс, можно поэксперементировать создавая столько процессов сколько необходимо. (На самом деле существует ограничение на количество выполняемых процессов, которыми JVM может эффективно управлять.)
Данная программа также показывает интересную статистику, поскольку можно наблюдать разницу в понижение производительности между различными реализациями процессов в JVM.