Ранее в этой главе я рекомендовал вам внимательно обдумать идею использования программной части или основного Frame
как реализации интерфейса Runnable
. При таком подходе можно использовать всего один поток в своём приложении, что ограничивает гибкость и может создать проблемы при необходимости работы с несколькими потоками такого типа.
Конечно, если требуется наследование от конкретного класса и желание сделать этот класс способным работать с потоками, то использование интерфейса Runnable
является правильным решением. Последний пример данной главы демонстрирует это, создавая класс RunnableCanvas
, который используется для отображения различных цветов (слово "canvas" означает "канвас", то есть "фон"). Это приложение было спроектировано таким образом, чтобы принимать аргументы командной строки для определения размера сетки цветов и длительности паузы между изменениями цвета (sleep()
).
//: ColorBoxes.java
// Использование интерфейса Runnable
import java.awt.*;
import java.awt.event.*;
``````markdown
публичный класс ColorBoxes расширяет Frame {
публичный ColorBoxes(int пауза, int сетка) {
setTitle("ColorBoxes");
setLayout(new GridLayout(сетка, сетка));
для (int i = 0; i < сетка * сетка; i++)
add(new CBox(пауза));
addWindowListener(new WindowAdapter() {
публичный void windowClosing(WindowEvent e) {
System. exit(0);
}
});
}
публичный статический void main(String[] аргументы) {
int пауза = 50;
int сетка = 8;
если (аргументы. length > 0)
пауза = Integer. parseInt(аргументы[0]);
если (аргументы. length > 1)
сетка = Integer. parseInt(аргументы[1]);
Frame f = new ColorBoxes(пауза, сетка);
f. setSize(500, 400);
f. setVisible(истинно);
}
}
}
///:~
ColorBoxes
— это типичное приложение, имеющее конструктор для установки графического интерфейса пользователя (GUI). Этот конструктор принимает два аргумента: первый — целочисленное значение int пауза
, которое задает время задержки между изменениями цвета, а второй — целочисленное значение int сетка
, которое используется для установки GridLayout
(сетка), где каждая ось имеет сетка
ячеек.
Затем он добавляет необходимое количество объектов CBox
, используемых для заполнения сетки, передавая каждому значение pause
. В методе main()
мы видим, как значения по умолчанию для pause
и grid
могут быть изменены (если они передаются в качестве параметров командной строки).CBox
является местом выполнения основной работы. Он наследует от класса Canvas
и реализует интерфейс Runnable
, что позволяет каждому объекту Canvas
также быть потоком (Thread
). При реализации Runnable
фактический объект Thread
не создаётся; это просто класс с методом run()
. Поэтому нам необходимо явно создать объект Thread
и передать ему объект Runnable
, после чего вызвать start()
(внутри конструктора). В CBox
этот поток называется t
.
Обратите внимание на массив colors
, который перечисляет все цвета из класса Color
. Он используется в методе newColor()
для генерации случайно выбранного цвета. Текущий цвет ячейки (элемента) — это cColor
.
Метод paint()
довольно прост — он просто устанавливает цвет равным cColor
и затем заливает весь холст этим цветом.
В методе run()
мы видим бесконечный цикл, который устанавливает cColor
как случайный цвет, затем вызывает repaint()
, чтобы показать его. После этого поток засыпает на время, указанное в командной строке.
Эта модель проектирования очень гибкая, и поскольку каждый поток тесно связан с каждым элементом Canvas
, теоретически можно создать любое количество потоков (хотя на практике это ограничивается количеством потоков, которое JVM может эффективно обрабатывать).
Это приложение также предоставляет интересный бенчмарк, так как оно демонстрирует значительные различия в скорости между различными механизмами JVM.## 14.5.1 Избыток потоков
Иногда мы обнаруживаем, что ColorBoxes
практически зависают. На моей собственной машине это происходит после создания сетки размером 10x10. Почему так? Конечно, есть основания заподозрить, что AWT делает что-то неправильно. Поэтому вот пример, который можно использовать для проверки этой догадки; он создает меньше потоков. Код был переорганизован таким образом, чтобы один Vector
реализовал Runnable
, а этот Vector
содержал множество цветных блоков и случайным образом выбирал некоторые из них для обновления. Затем мы создаем большое количество этих объектов Vector
, количество которых зависит от выбранной размерности сетки. В результате получается гораздо меньшее число потоков по сравнению с количеством цветных блоков. Так что если скорость работы увеличится, мы сразу это заметим, поскольку в предыдущем случае было слишком много потоков. Вот как это выглядит:
// Пример кода
public class ColorBoxExample {
public static void main(String[] args) {
Vector<ColorBlock> colorBlocks = new Vector<>();
// Добавляем цветные блоки в вектор
for (int i = 0; i < numberOfColorBlocks; i++) {
ColorBlock block = new ColorBlock();
colorBlocks.add(block);
// Создаем Runnable для каждого вектора
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// Обновление цветных блоков
updateColorBlocks(colorBlocks);
}
}
});
thread.start();
}
}
}
``````markdown
```java
private static void updateColorBlocks(Vector<ColorBlock> blocks) {
// Обновление цветных блоков
}
Примечание: Реальный код может отличаться, но данная структура демонстрирует идею использования одного Vector
, реализующего Runnable
.```
//: ColorBoxes2.java
// Балансировка использования потока
import java.awt.;
import java.awt.event.;
import java.util.*;
class CBox2 extends Canvas { private static final Color[] colors = { Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, 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 paint(Graphics g) { g.setColor(cColor); Dimension s = getSize(); g.fillRect(0, 0, s.width, s.height); } }
class CBoxVector extends Vector implements Runnable { private Thread t; private int pause; public CBoxVector(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)elementAt(i)).nextColor(); try { t.sleep(pause); } catch(InterruptedException e) {} } } }
public class ColorBoxes2 extends Frame {
private CBoxVector[] v;
public ColorBoxes2(int pause, int grid) {
setTitle("ColorBoxes2");
setLayout(new GridLayout(grid, grid));
v = new CBoxVector[grid];
for (int i = 0; i < grid; i++)
v[i] = new CBoxVector(pause);
for (int i = 0; i < grid * grid; i++) {
v[i % grid].addElement(new CBox2());
add((CBox2) v[i % grid].lastElement());
}
for (int i = 0; i < grid; i++)
v[i].go();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) {
// Краткая пауза по умолчанию вместо ColorBoxes:
int pause = 5;
int grid = 8;
if (args.length > 0)
pause = Integer.parseInt(args[0]);
if (args.length > 1)
grid = Integer.parseInt(args[1]);
Frame f = new ColorBoxes2(pause, grid);
f.setSize(500, 400);
f.setVisible(true);
}
}
///:~
```
```В `ColorBoxes2` мы создаем массив `CBoxVector`, инициализируем его таким образом, чтобы каждый `CBoxVector` располагался в сетке. Каждый элемент сетки знает, сколько времени ему следует "спать". Затем мы добавляем равное количество объектов `CBox2` в каждый `CBoxVector`. После этого каждый `Vector` передается методу `go()`, который запускает свой собственный поток. `CBox2` аналогичен `CBox` — способен отображаться случайным образом выбранным цветом. Но это всё, что может сделать `CBox2`. Все операции с потоками перемещены в `CBoxVector`.
`CBoxVector` также может иметь наследуемый поток `Thread`, а также иметь объект типа `Vector`. Преимуществом такого подхода является то, что методы `addElement()` и `elementAt()` могут принимать специфические параметры и типы возвращаемых значений, вместо общего `Object` (их названия также могут стать короче). Однако этот дизайн требует меньшего количества кода. Кроме того, он автоматически сохраняет все поведение `Vector`. Поскольку `elementAt()` требует много "закрытой" работы, используя множество скобок, то при увеличении основного объема кода, возможно, потребуется больше кода.Как и раньше, когда мы реализуем `Runnable`, мы не получаем всех функций, предоставляемых вместе с `Thread`, поэтому нам нужно создать новый `Thread` и передать себя ему через конструктор для официального "начала" — `start()` — чего-то. Это можно заметить в конструкторе `CBoxVector` и методе `go()`. Метод `run()` просто выбирает случайный номер элемента из `Vector` и вызывает `nextColor()` для этого элемента, чтобы выбрать новую случайную цветовую палитру.При запуске этой программы вы заметите, что она действительно работает быстрее и реагирует быстрее (например, при прерывании её выполнения). Также с увеличением размера сетки она реже будет зависеть. Таким образом, управление потоками теперь имеет ещё один важный аспект: проверка наличия "слишком большого" числа потоков (независимо от программы или платформы выполнения). При наличии слишком большого числа потоков следует попробовать использовать вышеописанные техники для балансировки числа потоков в программе. Если вы сталкиваетесь с проблемами производительности в многопоточной программе, вам придётся проверить несколько факторов:
(1) Достаточно ли часто вызываются `sleep`, `yield()`, и/или `wait()`?
(2) Достаточно ли длительны вызовы `sleep()`?
(3) Насколько велико количество выполняемых потоков?
(4) Проверено ли использование различных платформ и JVM?
Подобные вопросы являются одной из причин, почему разработка многопоточных приложений является сложной задачей.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )