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

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

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

14.3 Блокировка

Один поток может находиться в четырёх состояниях:

(1) Новый (New): объект потока создан, но ещё не запущен, поэтому он недоступен для выполнения.

(2) Готовый к выполнению (Runnable): это значит, что если система управления процессами имеет доступные циклы процессора, то этот поток может немедленно начать выполнение. Таким образом, поток может как быть активным, так и нет, но при условии, что все условия выполнены, ничто не препятствует его выполнению — он ни "умер", ни был "блокирован".

(3) Умерший (Dead): после завершения метода run(), поток считается "умершим". Также можно вызвать stop(), чтобы сделать его "умершим", но это приведёт к выбросу исключения, являющегося подклассом Error (то есть, мы обычно не ловим такие исключения). Вспомните, что выбрасывание исключений должно быть специальным событием, а не частью нормального выполнения программы. Поэтому использование stop() не рекомендуется (в Java 1.2 это категорически запрещено). Есть также метод destroy() (который никогда не будет реализован), который следует избегать, поскольку он очень жёсткий и не освобождает блокировки объекта.(4) Блокированный (Blocked): поток готов к выполнению, но что-то мешает ему. Если поток находится в состоянии блокировки, система управления процессами просто игнорирует его и не выделяет ему время процессора. Ни одна операция не будет выполнена до тех пор, пока поток снова не станет "готовым к выполнению".## 14.3.1 Почему происходит блокировка

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

(1) Вызов sleep(миллисекунды), который помещает поток в состояние "сон". В течение указанного времени поток не будет выполняться.

(2) Вызов suspend(), который приостанавливает выполнение потока. Поток вернётся в состояние "готового к выполнению" только после получения сообщения resume().

(3) Вызов wait(), который приостанавливает выполнение потока. Поток вернётся в состояние "готового к выполнению" только после получения сообщения notify() или notifyAll().

(4) Поток ожидает завершения некоторых операций ввода/вывода (I/O).(5) Поток пытается вызвать синхронизированный метод другого объекта, но этот объект заблокирован и временно недоступен. Также можно вызвать yield() (метод класса Thread) для добровольной передачи процессора другому потоку. Однако, если механизм планирования решит, что наш поток уже получил достаточно времени выполнения и перейдет к другому потоку, то это произойдет автоматически. То есть ничто не может помешать механизму планирования снова запустить наш поток. После того как поток был заблокирован, существуют причины, препятствующие его продолжительному выполнению. Приведённый ниже пример демонстрирует все пять способов вхождения потока в заблокированное состояние. Все они находятся в одном файле с названием Blocking.java, но здесь используются отдельные фрагменты для объяснения (можно заметить метки Continued и Continuing перед и после этих фрагментов. Используя инструменты, представленные в главе 17, эти фрагменты можно соединить вместе). Давайте рассмотрим базовый шаблон:```java //: Blocking.java // Пример различных способов блокировки потока. import java.awt.; import java.awt.event.; import java.applet.; import java.io.;

//////////// Базовая структура ////////////// class Blockable extends Thread { private Peeker peeker; protected TextField state = new TextField(40); protected int i; public Blockable(Container c) { c.add(state); peeker = new Peeker(this, c); } public synchronized int read() { return i; } protected synchronized void update() { state.setText(getClass().getName() + " состояние: i = " + i); } public void stopPeeker() { // peeker.stop(); Устарело в Java 1.2 peeker.terminate(); // Предпочтительный подход } }

class Peeker extends Thread { private Blockable b; private int session; private TextField status = new TextField(40); private boolean stop = false; public Peeker(Blockable b, Container c) { c.add(status); this.b = b; start(); } public void terminate() { stop = true; } public void run() { while (!stop) { status.setText(b.getClass().getName() + " Peeker " + (++session) + "; значение = " + b.read()); try { sleep(100); } catch (InterruptedException e){} } } } ///:Continued


Класс `Blockable` предназначен для использования как базового класса во всех остальных классах данного примера. Объект типа `Blockable` содержит поле `TextField` (текстовое поле) с именем `state`, которое используется для отображения информации о состоянии объекта. Метод, используемый для отображения этой информации, называется `update()`. Мы видим, что он использует `getClass().getName()` для получения имени класса, а не просто выводит его; это потому что метод `update()` не знает точного имени класса, который вызывает его, так как этот класс является производным от `Blockable`.
Внутри класса `Blockable` индикатор изменения представляет собой целое число `int i`; производные классы увеличивают его в своём методе `run()`. Для каждого объекта `Blockable` запускается отдельный поток класса `Peeker`. Задачей `Peeker` является вызов метода `read()`, проверка связанного с ним объекта `Blockable` на наличие изменений и последующий отчёт о результате проверки в виде текстового поля `status`. Обратите внимание, что методы `read()` и `update()` являются синхронными, что требует возможности свободного освобождения блокировки объекта — это очень важно. Сон


Первая тестовая проверка программы выполнена с помощью метода `sleep()`:

```markdown
///:Continuing
///////////// Блокировка через sleep() ///////////
class Sleeper1 extends Blockable {
  public Sleeper1(Container c) { super(c); }
  public synchronized void run() {
    while (true) {
      i++;
      update();
      try {
        sleep(1000);
      } catch (InterruptedException e) {}
    }
  }
}

class Sleeper2 extends Blockable {
  public Sleeper2(Container c) { super(c); }
  public void run() {
    while (true) {
      change();
      try {
        sleep(1000);
      } catch (InterruptedException e) {}
    }
  }
  public synchronized void change() {
    i++;
    update();
  }
} ///:Continued
```В классе `Sleeper1`, весь метод `run()` является синхронизированным. Мы видим, что связанный с ним объект `Peeker` работает корректно до запуска потока, после чего `Peeker` полностью прекращает работу. Это одна форма "блокировки": поскольку метод `Sleeper1.run()` синхронизирован, и как только поток начинается, он обязательно находится внутри этого метода, метод никогда не освобождает объект от блокировки, что приводит к блокировке потока `Peeker`.Класс `Sleeper2` предлагает решение путём установки неблокирующего режима работы. Только метод `change()` является синхронизированным, поэтому даже если `run()` находится внутри вызова `sleep()`, `Peeker` всё ещё может получить доступ к необходимому ему синхронизированному методу — `read()`. Здесь мы видим, что после запуска потока `Sleeper2`, `Peeker` продолжает работать.

### Пауза и восстановление

Далее в примере рассматривается концепция "паузы" или "приостановки" (`suspend`). Класс `Thread` предоставляет метод `suspend()`, который временно приостанавливает выполнение потока; а также метод `resume()`, который используется для возобновления выполнения потока после паузы. Очевидно, можно заключить, что `resume()` вызывается внешним потоком, отличным от приостановленного. В этом случае требуется использовать отдельный класс `Resumer` (восстановитель). У каждого класса, демонстрирующего процесс паузы/восстановления, есть свой соответствующий восстановитель. Пример ниже:

```markdown
///:Continuing
/////////// Блокировка через suspend() ///////////
class SuspendResume extends Blockable {
  public SuspendResume(Container c) {
    super(c);    
    new Resumer(this);
  }
}

class SuspendResume1 extends SuspendResume {
  public SuspendResume1(Container c) { super(c);}
  public synchronized void run() {
    while(true) {
      i++;
      update();
      suspend(); // Отмечен как устаревший в Java  Yöntem suspend() Java 1.2'de kullanılmıyor
    }
  }
}
class SuspendResume2 extends SuspendResume {
  public SuspendResume2(Container c) { super(c);}
  public void run() {
    while(true) {
      change();
      suspend(); // Ustalenen metot suspend() Java 1.2'de kullanılmıyor
    }
  }
  public synchronized void change() {
      i++;
      update();
  }
}
``````java
class Resumer extends Thread {
  private SuspendResume sr;
  public Resumer(SuspendResume sr) {
    this.sr = sr;
    start();
  }
  public void run() {
    while(true) {
       try {
        sleep(1000);
      } catch (InterruptedException e) {}
      sr.resume(); // Устарел в Java 1.2
    }
  }
} ///: Continued

SuspendResume1 также предоставил синхронизированную метод run(). Однако после запуска этого потока мы обнаруживаем, что связанный с ним Peeker застревает в "блокированном" состоянии, ожидая освобождения объектного замка, которое никогда не произойдет. В SuspendResume2 эта проблема решена тем, что вместо синхронизации всего метода run(), используется отдельный синхронизированный метод change().

Для Java Yöntemler suspend() ve resume() Java 1.2'de kullanılmaya başlanan bir versiyonda kullanılamaz hale getirildi, çünkü suspend() nesne kilidi kullanır ve bu onu çok hassas kılar "öbür tarafın kilitli olduğunu bekleyen" durumlarda. Başka bir deyişle, birçok nesnenin birbirini beklemesi sonucu programın tamamen durması olasıdır. Bunlar eski uygulamalarda hala görülebilir olsa da, yeni uygulamalar oluştururken bunların kullanımı kesinlikle önerilmez. Bu bölümün sonunda bu sorunla ilgili doğru yaklaşım açıklanacaktır.

(3) Ожидание и уведомление ```По результатам двух первых примеров, мы знаем, что ни sleep(), ни `suspend()` не освобождают блокировку при вызове этих методов. При необходимости использовать объектный замок, это важно помнить. С другой стороны, метод `wait()` освобождает блокировку при вызове, что позволяет выполнять другие синхронизированные методы объекта во время выполнения `wait()`. Однако в следующих двух классах видно, что метод `run()` является "синхронизированным". Во время выполнения `wait()`, `Peeker` всё ещё имеет полный доступ ко всем синхронизированным методам. Это происходит потому, что `wait()` освобождает объектный замок перед внутренним вызовом метода.Можно также заметить две формы метода `wait()`. Первая форма принимает один аргумент типа `long`, который представляет собой количество миллисекунд, аналогично тому, как это работает в `sleep()`: пауза на указанное время. Разница заключается в том, что в `wait()` объектный замок уже был освобожден, и он может свободно выйти из состояния `wait()`, так как `notify()` может принудительно завершить ожидание времени. Второй вариант не принимает никаких аргументов, что означает, что `wait()` будет продолжать выполнение до тех пор, пока не будет вызван метод `notify()`. Кроме того, он не прекратится сам по себе через какое-то время.Одним из уникальных свойств методов `wait()` и `notify()` является то, что они являются частью базового класса `Object`, в отличие от методов `sleep()`, `suspend()` и `resume()`, которые являются частью класса `Thread`. Хотя это может показаться странным — специализированные методы управления потоками находятся внутри общего базового класса — стоит отметить, что объектные замки также являются частью каждого объекта. Поэтому мы можем поместить вызов `wait()` внутрь любого синхронизированного метода независимо от того, выполняется ли управление потоками в этом классе или нет. В действительности, единственным местом, где можно вызвать `wait()`, является синхронизированный метод или блок кода. Если попытаться вызвать `wait()` или `notify()` внутри неконтейнерного метода, программа скомпилируется, но при запуске она выдаст исключение `IllegalMonitorStateException` ("текущий поток не является владельцем"). Обратите внимание, что методы `sleep()`, `suspend()` и `resume()` могут быть вызваны внутри неконтейнерного метода, так как они не требуют работы с замками.Методы `wait()` и `notify()` могут быть вызваны только для своих собственных замков. Код, пытающийся использовать неверные замки, компилируется, но генерирует такое же исключение `IllegalMonitorStateException`. Мы не можем обмануть систему использованием замка другого объекта, но можем заставить другой объект выполнять соответствующую операцию над своим собственным замком. Таким образом, один способ заключается в создании синхронизированного метода, который вызывает `notify()` для своего объекта. Однако в классе `Notifier` мы видим вызов `notify()` внутри синхронизированного метода:

synchronized(wn2) {
  wn2.notify();
}

где wn2 — это объект типа WaitNotify2. Несмотря на то, что этот метод не является частью класса WaitNotify2, он все равно получает замок объекта wn2. В данный момент вызов notify() для wn2 является легальным и не приведет к исключению IllegalMonitorStateException.

// Продолжение
/////////// Блокировка через wait() ///////////
class WaitNotify1 extends Blockable {
  public WaitNotify1(Container c) { 
    super(c); 
  }
  
  public synchronized void run() {
    while(true) {
      i++;
      update();
      
      try {
        wait(1000);
      } catch (InterruptedException e) {}
    }
  }
}

Класс WaitNotify2 наследует от блокируемого объекта Blockable. Конструктор принимает контейнер c, вызывает конструктор родительского класса и создает новый экземпляр класса Notifier.Метод run() в классе WaitNotify2 синхронизирован и выполняет бесконечный цикл, где счетчик i увеличивается на единицу, после чего происходит обновление состояния. Затем метод wait() позволяет объекту войти в состояние "ожидания", пока другой поток не вызовет метод notify().Класс Notifier наследует от класса Thread. Он получает объект WaitNotify2 и запускается при помощи метода start(). Внутри метода run() поток также выполняет бесконечный цикл, где каждые две секунды он вызывает метод notify() для объекта WaitNotify2, что приводит его обратно в активное состояние после того как он был в состоянии "ожидания".

Если требуется, чтобы поток ожидал изменения других условий (контролируемых вне самого потока), то обычно используется метод wait(). Этот метод позволяет поместить поток в состояние "сон" и одновременно "активно" ждать изменения условий. Поток будет пробужден только тогда, когда другой поток вызовет метод notify() или notifyAll(). Таким образом, этот механизм обеспечивает возможность синхронизации между потоками.

Блокировка ввода/вывода

Если поток данных должен ждать завершения операции ввода/вывода, он автоматически переходит в состояние "блокировки". В данном примере два класса работают вместе с общими объектами Reader и Writer (используются потоки Java 1.1). Однако, в тестовой модели создаются каналы передачи данных, позволяющие двум потокам безопасно передавать данные друг другу.

Класс Sender записывает данные в объект Writer и затем "спит" случайное время. Класс Receiver сам не использует методы sleep(), suspend() или wait(), но при выполнении метода read(), если нет доступных данных, он автоматически переходит в состояние "блокировки". Пример:``````markdown Эти два класса также отправляют информацию в свои поля state и изменяют значение `i`, чтобы `Peeker` знал, что поток всё ещё работает.

(5) Тестирование

К удивлению, основной класс апплета очень прост, поскольку большую часть работы уже выполнили с помощью шаблона Blockable. В общих чертах мы создаём массив объектов типа Blockable. Поскольку каждый объект является отдельным потоком, они начинают действовать после нажатия кнопки "start". Также есть другая кнопка и метод actionPerformed(), который используется для прекращения всех объектов типа Peeker. Из-за того, что Java 1.2 "не рекомендует" использовать метод stop() класса Thread, можно рассматривать этот вариант как альтернативный способ прекращения работы. ```Чтобы установить соединение между Sender и `Receiver`, мы создаем объекты `PipedWriter` и `PipedReader`. Обратите внимание, что `PipedReader in` должен быть связан с `PipedWriter out` через конструктор. После этого все данные, помещённые в `out`, могут быть извлечены из `in` — словно они передаются через трубопровод. Затем объекты `in` и `out` передаются в конструкторы `Receiver` и `Sender`; последний рассматривает их как произвольные типы `Reader` и `Writer` соответственно (то есть они преобразуются в родительский класс).Массив объектов типа `Blockable`, которыми управляет переменная `b`, не инициализируется сразу при объявлении, так как потоки данных, передаваемые через пайп, не могут быть установлены заранее (необходимость в блоках `try` станет препятствием):

public class Blocking extends Applet {
    private Button start = new Button("Start"), 
                     stopPeekers = new Button("Stop Peekers");
    private boolean started = false;
    private Blockable[] b;
    private PipedWriter out;
    private PipedReader in;

    public void init() {
        out = new PipedWriter();
        try {
            in = new PipedReader(out);
        } catch (IOException e) {}

        b = new Blockable[] {
            new Sleeper1(this),
            new Sleeper2(this),
            new SuspendResume1(this),
            new SuspendResume2(this),
            new WaitNotify1(this),
            new WaitNotify2(this),
            new Sender(this, out),
            new Receiver(this, in)
        };

        start.addActionListener(new StartL());
        add(start);

        stopPeekers.addActionListener(new StopPeekersL());
        add(stopPeekers);
    }

    class StartL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (!started) {
                started = true;
                for (int i = 0; i < b.length; i++) {
                    b[i].start();
                }
            }
        }
    }

    class StopPeekersL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            // Demonstration of the preferred alternative method instead of Thread.stop():
            for (int i = 0; i < b.length; i++) {
                b[i].stopPeeker();
            }
        }
    }

    public static void main(String[] args) {
        Blocking applet = new Blocking();
        Frame aFrame = new Frame("Blocking");
        aFrame.addWindowListener(new WindowAdapter() {

При необходимости завершите код, чтобы он был корректным и работал правильно. публичный void windowClosing(WindowEvent e) { System.exit(0); } }); aFrame.add(applet, BorderLayout.CENTER); aFrame.setSize(350, 550); applet.init(); applet.start(); aFrame.setVisible(истинно); } } ///:~

В методе `init()`, обратите внимание, что цикл проходит через весь массив и добавляет поле `state` и текстовое поле `peeker.status` для каждой страницы.После первой успешной инициализации потока типа Blockable, каждый такой поток автоматически создаёт и запускает свой Peeker. Поэтому мы можем наблюдать, как различные Peekers начинают работать до того, как будет запущен основной поток Blockable. Это очень важно, так как при запуске потока Blockable некоторые Peekers могут быть заблокированы и прекратят работу. Понимание этого поможет лучше понять концепцию "блокировки".

14.3.2 Жёсткое замедление (Дедлок)

Из-за того, что поток может войти в состояние блокировки, а объекты могут иметь синхронизированные методы — доступ к которым невозможен, пока не будет снята блокировка, — один поток вполне может ждать другой объект, который сам ждёт следующий объект и так далее. Самый страшный случай этой "цепочки ожиданий" — это когда последний объект ждёт первый! В этом случае все потоки окажутся в состоянии бесконечного взаимного ожидания и будут парализованы. Мы называем это "дедлоком". Хотя такое явление встречается нечасто, его отладка представляет собой большую проблему.

С точки зрения самого языка программирования, нет прямых средств для предотвращения дедлоков; требуется внимательный подход к дизайну, чтобы избежать таких ситуаций. Если вам придётся отлаживать программу с дедлоком, вы столкнётесь с значительными трудностями.(1) Противодействие Java 1.2 методам stop(), suspend(), resume() и destroy()

Чтобы уменьшить вероятность возникновения дедлоков, одним из шагов Java 1.2 является противодействие использованию методов stop(), suspend(), resume() и destroy() класса Thread.

Противодействие методу stop() связано с тем, что он небезопасен. Он снимает все блокировки, за которыми следует поток, и если объект находится в неконсистентном состоянии ("разрушении"), то другие потоки смогут проверять и модифицировать его в этом состоянии. Результатом становится сложная ситуация, которую сложно диагностировать. Поэтому следует избегать использования stop(), вместо него можно использовать метод, показанный в Blocking.java, где используется флаг для указания потоку остановиться путем выхода из своего метода run().

Если поток был заблокирован, например, во время ожидания ввода данных, обычно нельзя просто проверять флаг, как это сделано в Blocking.java. Однако даже в этих случаях следует избегать использования stop(), а вместо него использовать метод interrupt() класса Thread, чтобы остановить выполнение заблокированного кода.

//: Interrupt.java
// Альтернативный подход к использованию stop()
// когда поток заблокирован
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

class Blocked extends Thread {
  public synchronized void run() {
    try {
      wait(); // Блокирует
    } catch(InterruptedException e) {
      System.out.println("InterruptedException");
    }
    System.out.println("Выход из метода run()");
  }
}
``````java
public class Interrupt extends Applet {
  private Button
    interrupt = new Button("Перерыв");
  private Blocked blocked = new Blocked();
  public void init() {
    add(interrupt);
    interrupt.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          System.out.println("Кнопка нажата");
          if(blocked == null) return;
          Thread remove = blocked;
          blocked = null; // чтобы освободить его
          remove.interrupt();
        }
      });
    blocked.start();
  }
  public static void main(String[] args) {
    Interrupt applet = new Interrupt();
    Frame aFrame = new Frame("Перерыв");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(200,100);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~

Blocked.run() внутри метода wait() создает заблокированный поток. Когда мы нажимаем кнопку, ссылка на blocked (заблокированный) устанавливается в null, что позволяет сборщику мусора удалить ее, а затем вызывается метод interrupt() объекта. При первом нажатии кнопки мы видим нормальное завершение потока. Однако после того как больше нет доступных для "убийства" потоков, мы видим только сообщение о том, что кнопка была нажата. Методы suspend() и resume() имеют врожденную склонность к возникновению мертвыхlocks (мёртвых замков).Методы suspend() и resume() имеют врожденную склонность к возникновению злоключений смертельного замедления (deadlock). При вызове метода suspend(), целевой поток приостанавливается, но продолжает владеть всеми ранее захвачеными блоками. В этот момент ни один другой поток не может получить доступ к заблокированным ресурсам до тех пор, пока "приостановленный" поток не будет восстановлен. Любой поток, который пытается восстановить целевой поток и одновременно использовать любой из заблокированных ресурсов, грозит нежелательным смертельным замедлением (deadlock). Поэтому мы не должны использовать suspend() и resume(), а вместо этого следует внедрить в свой класс Thread метку, указывающую, что поток должен работать или быть приостановлен. Если метка указывает, что поток должен быть приостановлен, используется wait() для приведения его в состояние ожидания. Если метка указывает, что поток должен быть восстановлен, используется notify() для его восстановления. Мы можем модифицировать предыдущий Counter2.java, чтобы реально испытать это. Хотя эффект двух версий примерно одинаков, вы заметите значительные изменения в организации кода — все "слушатели" используют анонимные внутренние классы, и Thread является внутренним классом. Это делает программу немного удобнее для написания, так как она отменяет некоторые дополнительные записи в Counter2.java.

//: Suspend.java
```// Альтернативный подход к использованию suspend() 
// и resume(), которые были отмечены как устаревшие 
// в Java 1.2.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;```java
public class Suspend extends Applet {
    private TextField t = new TextField(10);
    private Button
        suspend = new Button("Приостановить"),
        resume = new Button("Продолжить");

    class Suspendable extends Thread {
        private int count = 0;
        private boolean приостановлено = false;

        public Suspendable() { start(); }

        public void fauxSuspend() {
            приостановлено = true;
        }

        public synchronized void fauxResume() {
            приостановлено = false;
            notify();
        }

        public void run() {
            while (true) {
                try {
                    sleep(100);
                    synchronized(this) {
                        while (приостановлено)
                            wait();
                    }
                } catch (InterruptedException e) {}
                t.setText(Integer.toString(count++));
            }
        }
    }

    private Suspendable ss = new Suspendable();

    public void init() {
        add(t);
        suspend.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ss.fauxSuspend();
            }
        });
        add(suspend);

        resume.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ss.fauxResume();
            }
        });
        add(resume);
    }

    public static void main(String[] args) {
        Suspend applet = new Suspend();
        Frame aFrame = new Frame("Приостановка");
        aFrame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        aFrame.add(applet, BorderLayout.CENTER);
        aFrame.setSize(300, 100);
        applet.init();
        applet.start();
        aFrame.setVisible(true);
    }
}
Флаг `приостановлено` в типе данных `Suspendable` используется для переключения состояния "вешено" или "приостановлено". Чтобы приостановить поток, достаточно вызвать метод `fauxSuspend()` и установить этот флаг как `true`. Определение состояния этого флага происходит внутри метода `run()`.
```Как уже упоминалось ранее в этой главе, метод `wait()` должен быть синхронизирован (`synchronized`), чтобы он мог использовать объектный замок. В методе `fauxResume()` флаг `suspended` устанавливается как `false`, а затем вызывается метод `notify()`. Поскольку это происходит внутри синхронизированного блока, `wait()` будет пробужден этим `notify()`. Поэтому метод `fauxResume()` также должен быть синхронизирован, чтобы получить объектный замок перед вызовом `notify()` (таким образом, объектный замок может быть использован тем `wait()`, который просыпается). При следовании этому стилю программирования можно избежать использования `wait()` и `notify()`. Метод `destroy()` класса `Thread` вообще не реализован; он похож на нереставрируемый метод `suspend()`, поэтому возникают те же проблемы смертельного замедления, что и при использовании `suspend()`. Тем не менее, этот метод не был официально осужден, возможно, его реализуют в будущих версиях Java (после OnClickListener 1. 2), чтобы использовать его в специальных случаях, где можно допустить риск возникновения проблем смертельного замедления.Люди могут удивиться тому, почему были реализованы эти методы, которые теперь "опровергаются". Причина этого заключается в том, что компания Sun Corporation в основном полагалась на мнение разработчиков при принятии решений относительно изменений в языке, а не на мнение маркетологов. Обычно разработчики лучше понимают суть языка, чем продавцы. После того как ошибки были сделаны, они смогли более объективно их признать. Это означает, что Java может продолжать развиваться, даже если это создает некоторые неудобства для программистов Java. По моему личному мнению, я предпочитаю сталкиваться с этими неудобствами,而不愿看到语言停滞不前。 

Перевод части текста, содержащейся в кавычках:

По моему личному мнению, я предпочитаю сталкиваться с этими неудобствами,而不愿看到语言停滞不前。
(по моему личному мнению, я предпочитаю сталкиваться с этими неудобствами, а не видеть, как язык остается неподвижным.)

Опубликовать ( 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