1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-thinking-in-java-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
14.5 回顾runnable.md 16 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 11.03.2025 09:15 d56454c

14.5 Обзор Runnable

Ранее в этой главе я рекомендовал вам внимательно обдумать идею использования программной части или основного 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 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-thinking-in-java-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-thinking-in-java-zh.git
oschina-mirror
wizardforcel-thinking-in-java-zh
wizardforcel-thinking-in-java-zh
master