Главная страница
Навигация по странице:

  • Тестирование методом Монте-Карло.

  • Средства тестирования многопоточного кода

  • Полные примеры кода Однопоточная реализация архитектуры «клиент/сервер» листинг А .3 .

  • Архитектура «клиент/сервер» с использованием потоков

  • Runnable clientHandler = new Runnable() {

  • Thread clientConnection = new Thread(clientHandler); clientConnection.start(); }389 org .jfree .date .SerialDate листинг Б .1 .

  • Создание, анализ ирефакторинг


    Скачать 3.16 Mb.
    НазваниеСоздание, анализ ирефакторинг
    Дата29.09.2022
    Размер3.16 Mb.
    Формат файлаpdf
    Имя файлаChistyj_kod_-_Sozdanie_analiz_i_refaktoring_(2013).pdf
    ТипКнига
    #706087
    страница42 из 49
    1   ...   38   39   40   41   42   43   44   45   ...   49
    Строка
    Описание
    10
    Создание экземпляра ClassWithThreadingProblem. Обратите внимание на необхо- димость использования ключевого слова final, так как ниже объект используется в анонимном внутреннем классе
    12–16
    Создание анонимного внутреннего класса, использующего экземпляр ClassWith-
    ThreadingProblem
    18
    Код выполняется количество раз, достаточное для демонстрации его некоррект- ности, но так, чтобы он не выполнялся «слишком долго». Необходимо выдержать баланс между двумя целями; сбои должны выявляться за разумное время. Подо- брать нужное число непросто, хотя, как мы вскоре увидим, его можно заметно сократить
    19
    Сохранение начального значения. Мы пытаемся доказать, что код ClassWith-
    ThreadingProblem некорректен. Если тест проходит, то он доказывает, что код некорректен. Если тест не проходит, то он не доказывает ничего
    20
    Итоговое значение должно быть на два больше текущего
    22–23
    Создание двух потоков, использующих объект, который был создан в строках 12–
    16. Два потока, пытающихся использовать один экземпляр ClassWithThreading-
    Problem, могут помешать друг другу; эту ситуацию мы и пытаемся воспроизвести.
    24–25
    Запуск двух потоков
    382

    Тестирование многопоточного кода
    383
    Строка
    Описание
    26–27
    Ожидание завершения обоих потоков с последующей проверкой результатов
    29
    Сохранение итогового значения
    31–32
    Отличается ли значение endingId от ожидаемого? Если отличается, вернуть признак завершения теста – доказано, что код работает некорректно. Если нет, попробовать еще раз
    35
    Если управление передано в эту точку, нашим тестам не удалось доказать не- корректность кода за «разумное» время. Либо код работает корректно, либо ко- личество итераций было недостаточным для возникновения сбойной комбинации
    Бесспорно, этот тест создает условия для выявления проблем многопоточного обновления . Но проблема встречается настолько редко, что в подавляющем боль- шинстве случаев тестирование ее попросту не выявит . В самом деле, для сколько- нибудь статистически значимого выявления проблемы количество итераций должно превышать миллион . Несмотря на это, за десять выполнений цикла из
    1 000 000 итераций проблема была обнаружена всего один раз . Это означает, что для надежного выявления сбоев количество итераций должно составлять около
    100 миллионов . Как долго вы готовы ждать?
    Даже если тест будет надежно выявлять сбои на одном компьютере, вероятно, его придется заново настраивать с другими параметрами для выявления сбоев на другом компьютере, операционной системе или версии JVM .
    А ведь мы взяли очень простую задачу . Если нам не удается легко продемон- стрировать некорректность кода в тривиальной ситуации, как обнаружить по- настоящему сложную проблему?
    Как ускорить выявление этого простейшего сбоя? И что еще важнее, как напи- сать тесты, демонстрирующие сбои в более сложном коде? Как узнать, что код некорректен, если мы даже не знаем, где искать? Несколько возможных идей .
    Тестирование методом Монте-Карло. Сделайте тесты достаточно гибкими, что- бы вы могли легко вносить изменения в их конфигурацию . Повторяйте тесты сно- ва и снова (скажем, на тестовом сервере) со случайным изменением параметров .
    Если в ходе тестирования будет обнаружена ошибка, значит, код некорректен .
    Начните писать эти тесты на ранней стадии, чтобы как можно ранее начать их выполнение на сервере непрерывной интеграции . Не забудьте сохранить набор условий, при котором был выявлен сбой .
    
    Выполняйте тесты на каждой целевой платформе разработки . Многократно .
    Непрерывно . Чем продолжительнее тесты работают без сбоев, тем выше ве- роятность, что:
    • код продукта корректен, либо
    • тестирования недостаточно для выявления проблем .
    Запускайте тесты на машинах с разной нагрузкой . Если вы сможете имитировать нагрузку, близкую к среде реальной эксплуатации, сделайте это .
    383

    384
    Приложение А . Многопоточность II
    Но даже после выполнения всех этих действий вероятность обнаружения много- поточных проблем в вашем коде оставляет желать лучшего . Самыми коваными оказываются проблемы, возникающие при определенных комбинациях условий, встречающихся один раз на миллиард . Такие проблемы – настоящий бич слож- ных систем .
    Средства тестирования
    многопоточного кода
    Компания IBM создала программу ConTest
    1
    , которая особым образом готовит классы для повышения вероятности сбоев в потоково-небезопасном коде .
    Мы не связаны ни с IBM, ни с группой разработки ConTest . Об этой программе нам рассказал один из коллег . Оказалось, что всего несколько минут использо- вания ConTest радикально повышают вероятность выявления многопоточных сбоев .
    Тестирование с использованием ConTest проходит по следующей схеме:
    
    Напишите тесты и код продукта . Проследите за тем, чтобы тесты были специ- ально спроектированы для имитации обращений от многих пользователей при переменной нагрузке, как упоминалось ранее .
    
    Проведите инструментовку кода тестов и продукта при помощи ConTest .
    
    Выполните тесты .
    Если вы помните, ранее сбой выявлялся примерно один раз за десять миллионов запусков . После инструментовки кода в ConTest сбои стали выявляться один раз за тридцать запусков . Таким образом, сбои в адаптированных классах стали вы- являться намного быстрее и надежнее .
    Заключение
    В этой главе мы предприняли очень краткое путешествие по огромной, нена- дежной территории многопоточного программирования . Наше знакомство с этой темой нельзя назвать даже поверхностным . Основное внимание уделялось мето- дам поддержания чистоты многопоточного кода, но если вы собираетесь писать многопоточные системы, вам придется еще многому научиться . Мы рекомендуем начать с замечательной книги Дуга Ли «Concurrent Programming in Java: Design
    Principles and Patterns» [Lea99, p . 191] .
    В этой главе рассматривались опасности многопоточного обновления, а также ме- тоды чистой синхронизации и блокировки, которые могут их предотвратить . Мы
    1
    http://www .haifa .ibm .com/projects/veri�cation/contest/index .html
    384

    Полные примеры кода
    385
    обсудили, как потоки могут повысить производительность систем с интенсивным вводом/выводом, а также конкретные приемы повышения производительности .
    Также была рассмотрена взаимная блокировка и чистые способы ее предотвраще- ния . Приложение завершается описанием стратегий выявления многопоточных проблем посредством инструментовки кода .
    Полные примеры кода
    Однопоточная реализация архитектуры
    «клиент/сервер»
    листинг А .3 . Server.java package com.objectmentor.clientserver.nonthreaded;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketException;
    import common.MessageUtils;
    public class Server implements Runnable {
    ServerSocket serverSocket;
    volatile boolean keepProcessing = true;
    public Server(int port, int millisecondsTimeout) throws IOException {
    serverSocket = new ServerSocket(port);
    serverSocket.setSoTimeout(millisecondsTimeout);
    }
    public void run() {
    System.out.printf("Server Starting\n");
    while (keepProcessing) {
    try {
    System.out.printf("accepting client\n");
    Socket socket = serverSocket.accept();
    System.out.printf("got client\n");
    process(socket);
    } catch (Exception e) {
    handle(e);
    }
    }
    }
    продолжение
    385

    386
    Приложение А . Многопоточность II
    листинг А .3 (продолжение)
    private void handle(Exception e) {
    if (!(e instanceof SocketException)) {
    e.printStackTrace();
    }
    }
    public void stopProcessing() {
    keepProcessing = false;
    closeIgnoringException(serverSocket);
    }
    void process(Socket socket) {
    if (socket == null)
    return;
    try {
    System.out.printf("Server: getting message\n");
    String message = MessageUtils.getMessage(socket);
    System.out.printf("Server: got message: %s\n", message);
    Thread.sleep(1000);
    System.out.printf("Server: sending reply: %s\n", message);
    MessageUtils.sendMessage(socket, "Processed: " + message);
    System.out.printf("Server: sent\n");
    closeIgnoringException(socket);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    private void closeIgnoringException(Socket socket) {
    if (socket != null)
    try {
    socket.close();
    } catch (IOException ignore) {
    }
    }
    private void closeIgnoringException(ServerSocket serverSocket) {
    if (serverSocket != null)
    try {
    serverSocket.close();
    } catch (IOException ignore) {
    }
    }
    }
    386
    org.jfree.date.SerialDate
    387
    листинг А .4 . ClientTest.java package com.objectmentor.clientserver.nonthreaded;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketException;
    import common.MessageUtils;
    public class Server implements Runnable {
    ServerSocket serverSocket;
    volatile boolean keepProcessing = true;
    public Server(int port, int millisecondsTimeout) throws IOException {
    serverSocket = new ServerSocket(port);
    serverSocket.setSoTimeout(millisecondsTimeout);
    }
    public void run() {
    System.out.printf("Server Starting\n");
    while (keepProcessing) {
    try {
    System.out.printf("accepting client\n");
    Socket socket = serverSocket.accept();
    System.out.printf("got client\n");
    process(socket);
    } catch (Exception e) {
    handle(e);
    }
    }
    }
    private void handle(Exception e) {
    if (!(e instanceof SocketException)) {
    e.printStackTrace();
    }
    }
    public void stopProcessing() {
    keepProcessing = false;
    closeIgnoringException(serverSocket);
    }
    void process(Socket socket) {
    if (socket == null)
    return;
    продолжение
    387

    388
    Приложение Б . Многопоточность II
    листинг А .4 (продолжение)
    try {
    System.out.printf("Server: getting message\n");
    String message = MessageUtils.getMessage(socket);
    System.out.printf("Server: got message: %s\n", message);
    Thread.sleep(1000);
    System.out.printf("Server: sending reply: %s\n", message);
    MessageUtils.sendMessage(socket, "Processed: " + message);
    System.out.printf("Server: sent\n");
    closeIgnoringException(socket);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    private void closeIgnoringException(Socket socket) {
    if (socket != null)
    try {
    socket.close();
    } catch (IOException ignore) {
    }
    }
    private void closeIgnoringException(ServerSocket serverSocket) {
    if (serverSocket != null)
    try {
    serverSocket.close();
    } catch (IOException ignore) {
    }
    }
    }
    листинг А .5 . MessageUtils.java package common;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    public class MessageUtils {
    public static void sendMessage(Socket socket, String message)
    throws IOException {
    OutputStream stream = socket.getOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(stream);
    oos.writeUTF(message);
    oos.flush();
    }
    388
    org.jfree.date.SerialDate
    389
    public static String getMessage(Socket socket) throws IOException {
    InputStream stream = socket.getInputStream();
    ObjectInputStream ois = new ObjectInputStream(stream);
    return ois.readUTF();
    }
    }
    Архитектура «клиент/сервер»
    с использованием потоков
    Перевод сервера на многопоточную архитектуру сводится к простому изменению функции process
    (новые строки выделены жирным шрифтом):
    void process(final Socket socket) {
    if (socket == null)
    return;
    Runnable clientHandler = new Runnable() {
    public void run() {
    try {
    System.out.printf("Server: getting message\n");
    String message = MessageUtils.getMessage(socket);
    System.out.printf("Server: got message: %s\n", message);
    Thread.sleep(1000);
    System.out.printf("Server: sending reply: %s\n", message);
    MessageUtils.sendMessage(socket, "Processed: " + message);
    System.out.printf("Server: sent\n");
    closeIgnoringException(socket);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    };
    Thread clientConnection = new Thread(clientHandler);
    clientConnection.start();
    }
    389

    org .jfree .date .SerialDate
    листинг Б .1 . SerialDate.Java
    1 /* ========================================================================
    2 * JCommon : библиотека классов общего назначения для платформы Java(tm)
    3 * ========================================================================
    4 *
    5 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    6 *
    7 * Информация о проекте: http://www.jfree.org/jcommon/index.html
    8 *
    9 * Библиотека распространяется бесплатно; вы можете свободно распространять
    10 * и/или изменять ее на условиях лицензии Lesser General Public License
    11 * в формулировке Free Software Foundation; либо версии 2.1 лицензии, либо
    12 * (на ваше усмотрение) любой последующей версии.
    13 *
    14 * Библиотека распространяется в надежде, что она будет полезна, но
    15 * БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, даже без подразумеваемой гарантии ПРИГОДНОСТИ
    16 * для КОНКРЕТНОЙ ЦЕЛИ. За подробностями обращайтесь к GNU Lesser General
    17 * Public License.
    18 *
    19 * Вы должны получить копию лицензии GNU Lesser General Public License
    20 * с этой библиотекой; если этого не произошло, обратитесь в Free Software
    21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
    22 * USA.
    23 *
    24 * [Java является зарегистрированной торговой маркой Sun Microsystems, Inc.
    25 * в Соединенных Штатах и других странах].
    26 *
    27 * ---------------
    28 * SerialDate.java
    29 * ---------------
    30 * (C) Copyright 2001-2005, by Object Refinery Limited.
    31 *
    32 * Автор: Дэвид Гилберт (для Object Refinery Limited);
    33 * Участники: -;
    34 *
    35 * $Id: SerialDate.java,v 1.7 2005/11/03 09:25:17 mungady Exp $
    Б
    390
    org.jfree.date.SerialDate
    391
    36 *
    37 * Изменения (начиная с 11 октября 2001)
    38 * --------------------------
    39 * 11.10.2001 : Реорганизация класса и его перемещение в новый пакет
    40 * com.jrefinery.date (DG);
    41 * 05.12.2001 : Добавление метода getDescription(), исключение класса
    42 * NotableDate (DG);
    43 * 12.12.2001 : После удаления класса NotableDate IBD требует наличия
    44 * метода setDescription() (DG); исправлены ошибки
    45 * в функциях getPreviousDayOfWeek(), getFollowingDayOfWeek()
    46 * и getNearestDayOfWeek() (DG);
    47 * 05.12.2001 : Исправление ошибки в классе SpreadsheetDate (DG);
    48 * 29.05.2002 : Перемещение констант месяцев в отдельный интерфейс
    49 * (MonthConstants) (DG);
    50 * 27.08.2002 : Исправление ошибки в addMonths(), спасибо N???levka Petr (DG);
    51 * 03.10.2002 : Исправление ошибок по информации Checkstyle (DG);
    52 * 13.03.2003 : Реализация Serializable (DG);
    53 * 29.05.2003 : Исправление ошибки в методе addMonths (DG);
    54 * 04.09.2003 : Реализация Comparable. Обновление Javadoс для isInRange (DG);
    55 * 05.01.2005 : Исправление ошибки в методе addYears() (1096282) (DG);
    56 *
    57 */
    58 59 package org.jfree.date;
    60 61 import java.io.Serializable;
    62 import java.text.DateFormatSymbols;
    63 import java.text.SimpleDateFormat;
    64 import java.util.Calendar;
    65 import java.util.GregorianCalendar;
    66 67 /**
    68 * Абстрактный класс, определяющий требования для манипуляций с датами
    69 * без привязки к конкретной реализации.
    70 *
    71 * Требование 1 : совпадение с представлением дат в формате Excel;
    72 * Требование 2 : класс должен быть неизменным;
    73 *
    74 * Почему не использовать java.util.Date? Будем использовать, где это имеет смысл.
    75 * Класс java.util.Date бывмает *слишком* точным - он представляет момент
    76 * времени с точностью до 1/100 секунды (при этом сама дата зависит от часового
    77 * пояса). Иногда бывает нужно просто представить конкретный день (скажем,
    78 * 21 января 2015), не заботясь о времени суток, часовом поясе и т.д.
    79 * Именно для таких ситуаций определяется класс SerialDate.
    80 *
    81 * Вы можете вызвать getInstance() для получения конкретного субкласса
    82 * SerialDate, не беспокоясь о реализации.
    83 *
    84 * @author David Gilbert
    85 */
    продолжение
    391

    392
    Приложение Б . org.jfree.date.SerialDate
    листинг Б .1 (продолжение)
    86 public abstract class SerialDate implements Comparable,
    87 Serializable,
    88 MonthConstants {
    89 90 /** Для сериализации. */
    91 private static final long serialVersionUID = -293716040467423637L;
    92 93 /** Символические обозначения формата даты. */
    94 public static final DateFormatSymbols
    95 DATE_FORMAT_SYMBOLS = new SimpleDateFormat().getDateFormatSymbols();
    96 97 /** Порядковый номер для 1 января 1900. */
    98 public static final int SERIAL_LOWER_BOUND = 2;
    99 100 /** Порядковый номер для 31 декабря 9999. */
    101 public static final int SERIAL_UPPER_BOUND = 2958465;
    102 103 /** Наименьшее значение года, поддерживаемое форматом даты. */
    104 public static final int MINIMUM_YEAR_SUPPORTED = 1900;
    105 106 /** Наибольшее значение года, поддерживаемое форматом даты. */
    107 public static final int MAXIMUM_YEAR_SUPPORTED = 9999;
    108 109 /** Константа для понедельника, эквивалент java.util.Calendar.MONDAY. */
    110 public static final int MONDAY = Calendar.MONDAY;
    111 112 /**
    113 * Константа для вторника, эквивалент java.util.Calendar.TUESDAY.
    114 */
    115 public static final int TUESDAY = Calendar.TUESDAY;
    116 117 /**
    118 * Константа для среды, эквивалент
    119 * java.util.Calendar.WEDNESDAY.
    120 */
    121 public static final int WEDNESDAY = Calendar.WEDNESDAY;
    122 123 /**
    124 * Константа для четверга, эквивалент java.util.Calendar.THURSDAY.
    125 */
    126 public static final int THURSDAY = Calendar.THURSDAY;
    127 128 /** Константа для пятницы, эквивалент java.util.Calendar.FRIDAY. */
    129 public static final int FRIDAY = Calendar.FRIDAY;
    130 131 /**
    132 * Константа для субботы, эквивалент java.util.Calendar.SATURDAY.
    133 */
    134 public static final int SATURDAY = Calendar.SATURDAY;
    135 392
    org.jfree.date.SerialDate
    1   ...   38   39   40   41   42   43   44   45   ...   49


    написать администратору сайта