Думай на Java

Одно из изменений, которое было


Одно из изменений, которое было сделано в Java2 для уменьшения возможности возникновения мертвых блокировок заключалось в запрещении для Thread методов stop(), suspend(), resume() и destroy( ).

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

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

//: c14:Interrupt.java

// The alternative approach to using

// stop() when a thread is blocked.

// <applet code=Interrupt width=200 height=100>

// </applet>

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

class Blocked extends Thread { public synchronized void run() { try { wait(); // Blocks

} catch(InterruptedException e) { System.err.println("Interrupted"); } System.out.println("Exiting run()"); } }

public class Interrupt extends JApplet { private JButton interrupt = new JButton("Interrupt"); private Blocked blocked = new Blocked(); public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(interrupt); interrupt.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Button pressed"); if(blocked == null) return; Thread remove = blocked; blocked = null; // to release it

remove.interrupt(); } }); blocked.start(); } public static void main(String[] args) { Console.run(new Interrupt(), 200, 100); } } ///:~



Метод wait() внутри Blocked.run() блокирует процесс. Когда вы нажимаете кнопку, ссылка blocked установлена в null, так что сборщик мусора удаляет ее, после чего для этого объекта вызывается метод interrupt(). Первый раз когда вы нажимаете кнопку видно, что процесс завершается, когда процессов для завершения не останется кнопка останется в нажатом состоянии.

Методы suspend() и resume() по умолчанию являются склонными к созданию мертвых блокировок. Когда вызывается suspend() целевой процесс останавливается, но он все равно может получить блокировку установленную в этот момент. Таким образом, ни один ни другой процесс не сможет получить доступ к блокированным ресурсам пока процесс не разблокируется. Любой процесс, который хочет разблокировать целевой процесс и также пытается использовать любой из заблокированных ресурсов приведет к мертвой блокировке. Вы не должны использовать suspend() и resume(), а вместо этого следует установить флаг в ваш класс Thread для отображения того факта должен ли быть процесс активным или временно приостановлен.  процесс переход в ожидание используя wait(). Когда флаг показывает, что процесс должен быть возобновлен процесс перезапускается с помощью notify(). Пример может быть создан с помощью переделки Counter2.java. Хотя эффект одинаков, можно заметить, что сам код совершенно отличен ў анонимные внутренние классы используются для всех слушателей, а также Thread является внутренним классом, что делает программирование немного более удобным поскольку это предотвращает учета дополнительно использованных системных ресурсов необходимых в Counter2.java: //: c14:Suspend.java

// The alternative approach to using suspend()

// and resume(), which are deprecated in Java 2.

// <applet code=Suspend width=300 height=100>

// </applet>

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

public class Suspend extends JApplet { private JTextField t = new JTextField(10); private JButton suspend = new JButton("Suspend"), resume = new JButton("Resume"); private Suspendable ss = new Suspendable(); class Suspendable extends Thread { private int count = 0; private boolean suspended = false; public Suspendable() { start(); } public void fauxSuspend() { suspended = true; } public synchronized void fauxResume() { suspended = false; notify(); } public void run() { while (true) { try { sleep(100); synchronized(this) { while(suspended) wait(); } } catch(InterruptedException e) { System.err.println("Interrupted"); } t.setText(Integer.toString(count++)); } } } public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); suspend.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { ss.fauxSuspend(); } }); cp.add(suspend); resume.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { ss.fauxResume(); } }); cp.add(resume); } public static void main(String[] args) { Console.run(new Suspend(), 300, 100); } } ///:~



Флаг suspended внутри Suspendable используется для включения или отключения временной приостановки. Для приостановки флаг устанавливается в true через вызов fauxSuspend() и это определяется внутри run(). wait(), как было описано в этом разделе раннее, должен быть synchronized, так что он может иметь блокировку объекта. В fauxResume(), флаг suspended устанавливается в false и вызывается notify(), поскольку это разбудит wait() внутри блока synchronized, то метод fauxRsume() должен быть также объявлен как synchronized так, что он получает блокировку до вызова notify() (таким образом блокировка доступна для wait() чтобы проснуться). Если следовать стилю этой программы, то можно избежать использования suspend() и resume().

Метод destroy( ) для Thread никогда не будет реализован; это аналогично suspend() который не может продолжить выполнение, и поэтому он имеет те же самые склонности к мертвой блокировке как и suspend(). Однако это не запрещенный (deprecated) метод и может быть реализован в следующих версиях Java (после 2) для специальных ситуаций, в которых риск мертвой блокировки приемлем.

Можно удивляться, почему эти методы, в настоящее время запрещенные, были включены в Java в начале. Похоже была допущена довольно существенная ошибка чтобы просто полностью убрать их (и сделать еще один прокол в аргументации об особенном дизайне Java и в агитации безотказной работы меркетологами Sun). Слова же в поддержку изменений заключается в том, что это ясно показывает, что программисты, а не маркетологи играют в спектакль - одни находят проблемы, другие исправляют их. Я считаю это более перспективным и обнадеживающим, чем уход от проблемы только из-за того, что "исправление ошибки приводи к ошибке". Это также означает, что Java продолжает улучшаться, даже если это вызывает дискомфорт у части Java программистов. Уж лучше я буду испытывать временный дискомфорт чем наблюдать застой языка.


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