god commit with all final work

This commit is contained in:
Wayne H. Shephard 2024-05-12 20:23:51 +03:00
parent 9ac3bf95f1
commit 628dcd7607
64 changed files with 10309 additions and 39 deletions

Binary file not shown.

View File

@ -4,13 +4,18 @@
%\documentclass[a4paper,12pt]{report}
% article is for single section compilation
\documentclass[a4paper,12pt]{article}
\documentclass[a4paper]{article}
\usepackage[english,russian]{babel}
\usepackage{ifthen}
\input{settings/main-style-preamble}
\input{settings/fancy-listings-preamble}
\titleformat{\chapter}[block]{\larger[4]\bfseries}{\thechapter.~}{.5em}{}
\titleformat{\section}[block]{\larger[3]\bfseries}{\thesection.~}{.5em}{}
\titleformat{\subsection}[block]{\larger[2]\bfseries}{\thesubsection.~}{.5em}{}
\titleformat{\subsubsection}[block]{\larger\bfseries}{\thesubsubsection.~}{.5em}{}
\graphicspath{{./pics/}}
\title{Техническая специализация Java}
\author{Иван Игоревич Овчинников}
@ -22,6 +27,7 @@
\usepackage{subfiles}
\begin{document}
\fontsize{12}{12}\selectfont
\maketitle
\ifx\isSingle\undefined
@ -51,13 +57,17 @@
\ifx\isSingle\undefined
\chapter{Java Development Kit}
\fi
\subfile{jtd1-06-abstract}
\newpage
\subfile{jtd2-07-abstract}
\newpage
\subfile{jtd3-08-abstract}
\newpage
% \subfile{jtd6-11a}
\appendix
\setcounter{secnumdepth}{5}
\ifx\isSingle\undefined
\printnomenclature[40mm]
\appendix
\chapter*{Семинары}
\addcontentsline{toc}{chapter}{Семинары}
@ -72,5 +82,9 @@
\subfile{jtc4-04-workshop}
\newpage
\subfile{jtc5-05-workshop}
\newpage
\subfile{jtd1-06-workshop}
\printnomenclature[40mm]
\end{document}

879
jtd1-06-abstract.tex Normal file
View File

@ -0,0 +1,879 @@
\documentclass[j-spec.tex]{subfiles}
\begin{document} \sloppy
\setcounter{section}{5}
\setlength{\columnsep}{22pt}
\pagestyle{plain}
\tableofcontents
\section{Инструментарий: GUI -- графический интерфейс пользователя}
\subsection*{В предыдущей главе}
\begin{itemize}
\item ООП;
\item процедурное программирование;
\item исключения;
\item устройство языка Java;
\item устройство платформы для создания и запуска приложений на JVM-языках;
\item базовые средства ввода-вывода;
\item базовые терминальные приложения;
\item алгоритмические задачи, не требующие сложных программных решений.
\end{itemize}
\subsection*{В этом разделе}
Интерфейс пользователя -- это важно, поскольку это именно то, что видят пользователи в первую очередь.
\begin{frm} \excl
Это самое сложное занятие начального этапа, но не потому что оно содержит очень сложную информацию, а потому что заставит под необычным углом взглянуть на те принципы ООП, которые уже известны.
\end{frm}
Будут рассмотрены вопросы создания окон, менеджеров размещений, элементов графического интерфейса, и обработчиков событий.
\begin{itemize}
\item \nom{Swing}{библиотека для создания графического интерфейса для программ на языке Java. Swing разработан компанией Sun Microsystems и содержит ряд графических компонентов (Swing widgets), таких как кнопки, поля ввода, таблицы и тому подобные.};
\item \nom{Асинхронность}{вид программирования, позволяющий вынести выполняемые задачи отдельными блоками кода. Применяется в сервисах, где предыдущее действие тормозит следующее. Синхронный процесс выполняется поэтапно. Пользователь совершает действие, ждёт, пока программа обработает часть кода, переходит к другому блоку. При использовании асинхронности, код убирает операцию, блокирующую следующие действия.};
\item \nom{Параллельность}{способ организации компьютерных вычислений, при котором программы разрабатываются, как набор взаимодействующих вычислительных процессов, работающих асинхронно и при этом одновременно. Параллельное программирование -- это техника программирования, которая использует преимущества многоядерных или многопроцессорных компьютеров и является подмножеством более широкого понятия многопоточности (multithreading).};
\item \nom{Окно}{это прямоугольная область экрана, в котором приложение отображает информацию и получает реакцию от пользователя. Одновременно на экране может отображаться несколько окон, в том числе, окон других приложений, однако лишь одно из них может получать реакцию от пользователя -- активное окно. Пользователь использует клавиатуру, мышь и прочие устройства ввода для взаимодействия с приложением, которому принадлежит активное окно.};
\item \nom{Компонент}{независимый модуль программы, предназначенный для повторного использования. Часто компоненты объединяют по общим признакам и организовывают в соответствии с определёнными правилами и ограничениями. Например, компоненты графического интерфейса.};
\item \nom{Компоновщик}{Компоновщик (layout manager) в библиотеке Java Swing отвечает за расположение и организацию компонентов (например, кнопок, текстовых полей, панелей). Он определяет, как компоненты будут выравниваться, размещаться и изменяться при изменении размеров окна.};
\item \nom{Панель}{Панель в библиотеке Java Swing представляет собой контейнер, который используется для группировки и организации других компонентов в пользовательском интерфейсе. Она представляет собой область с фиксированным размером на которую можно добавить другие компоненты, такие как кнопки, текстовые поля или изображения.};
\item \nom{События}{это действия или случаи, возникающие в программируемой системе, о которых система сообщает для того, чтобы было возможно с ними взаимодействовать. Например, если пользователь нажимает кнопку на графическом интерфейсе, возможно ответить на это действие, отобразив информационное окно.};
\item \nom{Обработчик}{это механизм, с помощью которого приложение может перехватить события, такие как сообщения, действия мыши и нажатия клавиш. Функция, которая перехватывает события определенного типа, называется процедурой-обработчиком. Процедура-обработчик может действовать для каждого получаемого события, а затем изменить или отменить событие.}.
\end{itemize}
\subsection{Почему именно Swing?}
Вместо вступления следует кратко ответить на самый популярный вопрос.
\begin{itemize}
\item[\faRemove] это популярный и современный фреймворк;
\item[\faRemove] пригодится любому программисту на Java;
\item[\faCheck] поможет лучше понять ООП;
\item[\faCheck] работа композиции из объектов;
\item[\faCheck] обмен информацией между объектами;
\item[\faCheck] явно использует ссылочную природу данных;
\item[\faCheck] улучшает запоминание базовых взаимосвязей объектов;
\item[\faCheck] без искусственных примеров (в результате будет разработана простая игра, крестики-нолики с графическим интерфейсом).
\end{itemize}
\begin{frm}
\faQuestion~ Почему не JavaFX? Фреймворк был выведен из стандартной библиотеки языка, начиная с Java 9, и достаточно сложен для базового знакомства с графическими библиотеками.
\faQuestion~ Почему не LibGDX? Фреймворк является надстройкой над Swing, объясняя его всё равно необходимо будет объяснять Swing/AWT.
\faExclamation~ Intellij IDEA — написана на Swing.
\end{frm}
\subsection{JFrame: Главный класс окна}
\subsubsection{Создание окна}
В этом разделе происходит изучение программирования и ООП, на примере и с использованием фреймворка Swing, поэтому необходимо сосредоточиться на объектах, их свойствах и взаимосвязях.
\begin{frm} \info
Изучение фреймворка Swing -- не основная задача раздела, поэтому важно помнить, что не нужно зазубривать все названия всех компонентов.
\end{frm}
Окно графического интерфейса, как и любая другая программа -- это класс и объекты. В листинге \hrf{lst:init-game-window} создаётся новый класс с названием \code{GameWindow}. Для начала, необходимо получить доступ к методам, содержащимся в библиотеке, для этого применяется наследование от класса \code{JFrame}, и создаётся конструктор.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Класс окна},label={lst:init-game-window}]
package ru.gb.jdk.one.online;
import javax.swing.*;
public class GameWindow extends JFrame {
GameWindow() {
//...
}
}
\end{lstlisting}
В основном классе программы просто создаётся новый объект вновь описанного класса с окном. С точки зрения программирования и ООП не происходит ничего значительно отличающегося от котиков и пёсиков. Если запустить получившееся приложение, оно должно запуститься, и сразу завершиться, это признак верно написанного кода.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Создание нового окна}]
package ru.gb.jdk.one.online;
public class Main {
public static void main(String[] args) {
new GameWindow();
}
}
\end{lstlisting}
\subsubsection{Закрытие окна, завершение приложения}
Б\'{о}льшая часть свойств окна не меняется за всё время существования окна и задаётся в конструкторе, то есть окно при создании будет наделено какими-то свойствами, которые в некоторых случаях можно будет поменять во время работы приложения. Самое не очевидное на первый взгляд то, что в Swing при нажатии на крестик в углу окна программа не завершается. По умолчанию, все создаваемые окна -- невидимые. Это сделано потому что есть возможность создавать сколько угодно окон для приложения и такое поведение значительно снижает риск неожиданного поведения.
\begin{frm} \excl
Все окна по умолчанию невидимые. Нажатие на крестик по умолчанию делает окно невидимым, а не завершает программу.
\end{frm}
Для того, чтобы программа закрылась, необходимо принять решение, какое окно в ней будет главным. Чтобы программа завершалась при закрытии главного окна (обычно, это первое, что делается при создании одно оконных приложений) экземпляру JFrame устанавливается свойство \code{DefaultCloseOperation}. То есть устанавливается, что нужно сделать, когда это (главное) окно закроется. В это свойство записывается константа \code{EXIT_ON_CLOSE}. Если этого не сделать, то будет использовано поведение по умолчанию, окно сделается невидимым, и приложение не завершится.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Завершение приложения по закрытию окна}]
setDefaultCloseOperation(EXIT_ON_CLOSE);
\end{lstlisting}
\subsubsection{Свойства окна}
Добавив констант\footnote{Считается хорошим тоном не держать в коде никаких «магических цифр», чтобы не думать, что они значат и почему они именно такие, и что будет если их поменять.} с шириной, высотой и положением окна по осям относительно рабочего стола, становится возможным настроить размеры и положение окна в конструкторе.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Базовые свойства окна}]
private static final int WINDOW_HEIGHT = 555;
private static final int WINDOW_WIDTH = 507;
private static final int WINDOW_POSX = 800;
private static final int WINDOW_POSY = 300;
GameWindow() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(WINDOW_POSX, WINDOW_POSY);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setVisible(true);
}
\end{lstlisting}
Установка всех начальных свойств окна осуществляется вызовом соответствующих сеттеров в конструкторе. По умолчанию окно -- невидимое, поэтому для его демонстрации в конструкторе вызывается метод \code{setVisible} с передаваемым аргументом \code{true}.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-01-jframe-01.png}
\caption{Пустое окно указанного размера}
\end{figure}
Окно -- это всегда \textit{отдельный поток программы}, внутри которого работает бесконечный цикл. В объекте окна существует очередь сообщений, которую цикл опрашивает и выполняет.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Пример сообщения, показывающего многопоточность}]
public static void main(String[] args) {
new GameWindow();
System.out.println("Method main() is over");
}
\end{lstlisting}
Запустив программу и внимательно изучив результат, очевидно, что окно создалось, в консоли видно, что работа метода \code{main} закончилась, а окно всё равно выполняется, его, при желании, можно подвигать, изменить размер и так далее. Это и есть наглядная демонстрация \textit{многопоточности}. Таким образом, получается, что когда создаётся новое окно -- нет необходимости его ни в какой контейнер помещать, ни думать, как оно будет взаимодействовать с пользователем, оно создастся и будет жить своей жизнью. \textbf{Инкапсуляция}. Если появится необходимость что-то ещё выполнить в методе \code{main}, запрета на написание действий не существует и действия будут выполняться \textit{параллельно}, \textit{асинхронно}.
\subsubsection{Вопросы для самопроверки}
\begin{enumerate}
\item Почему в классе \code{GameWindow} доступны методы фреймворка?
\begin{enumerate}
\item Из-за устройства фреймворка Swing;
\item из-за наследования от JFrame;
\item из-за импорта классов Swing.
\end{enumerate}
\item Чтобы создать пустое окно в программе нужно
\begin{enumerate}
\item импортировать библиотеку Swing;
\item создать класс MainWindow;
\item создать класс-наследник JFrame.
\end{enumerate}
\item Свойства окна, такие как размер и заголовок возможно задать
\begin{enumerate}
\item написанием методов в классе-наследнике;
\item вызовом методов в конструкторе;
\item созданием констант в первых строках класса.
\end{enumerate}
\end{enumerate}
\subsection{Компоненты окна}
\subsubsection{Кнопка}
Для того, чтобы точно ничего не перепутать в процессе разработки, возможно придать окну больше индивидуальности, задав заголовок и запретив пользователю изменять его размеры, для игры в крестики-нолики это будет важно, чтобы красиво отображалось поле. Для этого вызовем методы \code{setTitle()} и \code{setResizeable()}, соответственно.
\begin{frm} \info
Элементы графического интерфейса -- это хорошо знакомые кнопки, текстовые поля, надписи (лейблы), и тому подобные.
\end{frm}
За кнопки отвечает класс \code{JButton}, при создании экземпляра есть возможность сразу в конструкторе задать надпись, которая будет отображаться на кнопке. Сразу создадим несколько кнопок, например, «выход». Кнопки недостаточно просто создать, поскольку неизвестно, где они должны находиться. Одну из созданных кнопок добавим на окно -- для этого внутри конструктора необходимо воспользоваться методом \code{add()}, который требует в качестве аргумента передать ему какой-то \code{Component}. Все кнопки, лейблы и прочие элементы интерфейса -- это наследники класса \code{Component}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Компонент «Кнопка»}]
JButton btnStart = new JButton("New Game");
JButton btnExit = new JButton("Exit");
GameWindow() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(WINDOW_POSX, WINDOW_POSY);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setTitle("TicTacToe");
setResizable(false);
add(btnStart);
setVisible(true);
}
\end{lstlisting}
После добавления в конструкторе кнопки, при запуске приложения видно, что она заняла всё окно приложения. Если убрать вызов метода \code{setResizeable()}, то также возможно удостовериться, что при изменении размеров окна, размер кнопки также будет меняться.
\begin{figure}[H]
\centering
\begin{subfigure}[b]{0.47\textwidth}
\centering
\includegraphics[width=\textwidth]{jd-01-btn-01.png}
\caption{ }
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.47\textwidth}
\centering
\includegraphics[width=\textwidth]{jd-01-btn-02.png}
\caption{ }
\end{subfigure}
\caption{Изменение размеров кнопки в следствие изменения размеров окна}
\end{figure}
При попытке добавить вторую кнопку на это же окно очевидно (рис. \hrf{pic:btn-overhead}), что вторая кнопка полностью перекрыла первую.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Вторая кнопка}]
add(btnExit);
\end{lstlisting}
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-01-btn-03.png}
\caption{Перекрытие компонентов}
\label{pic:btn-overhead}
\end{figure}
\subsubsection{Компоновщики (менеджеры размещений)}
Перекрытие происходит из-за использования компоновщика, или, как их ещё называют, менеджера размещений.
\begin{frm} \info
Менеджеры размещений нужны для того, чтобы не думать каждый раз о том, как изменится размер и координаты конкретного элемента, допустим, при изменении окна и не писать сложное поведение вложенных компонентов чтобы просто отобразить то, что привычно пользователю.
\end{frm}
Компоновщики активно используются в любом программировании графических интерфейсов в любых языках программирования, от C++ до JavaScript, потому что это достаточно удобный механизм, берущий на себя значительный пласт работы. Использование компоновщиков позволяет эффективно управлять и размещать компоненты в окне или панели пользовательского интерфейса, обеспечивая гибкость и адаптивность приложения к изменениям размеров и расположения компонентов на экране.
Компоновщик -- это специальный объект, который помещается на некоторые (\code{RootPaneContainer}\footnote{\href{https://docs.oracle.com/javase/tutorial/uiswing/layout/index.html}{docs.oracle.com: Компоновщики}}) компоненты и осуществляет автоматическую расстановку добавляемых в него компонентов, согласно правилам. Компоновщиков существует несколько типов, каждый из которых предоставляет свои специфические возможности и алгоритмы расположения. Компоновщик выбирается в зависимости от требуемых задач и желаемого внешнего вида интерфейса.
\begin{itemize}
\item \code{BorderLayout} (по умолчанию);
\item \code{BoxLayout};
\item \code{CardLayout};
\item \code{FlowLayout};
\item \code{GridBagLayout};
\item \code{GridLayout};
\item \code{GroupLayout};
\item \code{SpringLayout}.
\end{itemize}
По умолчанию в Swing используется компоновщик \code{BorderLayout} (рис. \hrf{pic:layout-border}). Он располагает всё, что ему передаётся в центре, но также у него есть ещё четыре положения, маленькие области по краям.
\begin{figure}[H]
\centering
\fontsize{12}{1}\selectfont
\begin{subfigure}[b]{0.32\textwidth}
\centering
\includesvg[scale=1.01]{pics/jd-01-border-layout.svg}
\caption{\code{BorderLayout}}
\label{pic:layout-border}
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.32\textwidth}
\centering
\includesvg[scale=1.01]{pics/jd-01-flow-layout.svg}
\caption{\code{FlowLayout}}
\label{pic:layout-flow}
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.32\textwidth}
\centering
\includesvg[scale=1.01]{pics/jd-01-grid-layout.svg}
\caption{\code{GridLayout}}
\label{pic:layout-grid}
\end{subfigure}
\caption{Популярные менеджеры размещений}
\end{figure}
Если какая-то область не занята компонентом, она автоматически уменьшается до нулевых размеров, оставляя место другим компонентам. Поэтому, если необходимо какой-то компонент расположить не в центре, это нужно явно указать при добавлении. На первый взгляд, это немного не очевидно, поэтому лучше запомнить, что при добавлении надо указать ещё один параметр, константу, например, \code{BorderLayout.SOUTH}. \code{FlowLayout} будет располагать элементы друг за другом слева направо, сверху вниз. Компоновщик-сетка \code{GridLayout} при создании принимает на вход число строк и столбцов и располагает компоненты в получившейся сетке.
\begin{frm} \info
Основная идея, которую надо понять, это не названия компоновщиков, а то, что в Swing вся работа происходит через компоновщики -- Layout, которые каждый по-своему располагают элементы в окне.
\end{frm}
\subsubsection{Панель для размещения JPanel}
Разнообразие требований к разработке графических интерфейсов может привести к необходимости создания бесконечного числа компоновщиков. Поэтому разработчики библиотеки Swing придумали использовать не только компоненты сами по себе, но и группы элементов, которые располагаются на так называемых панелях (\code{JPanel}). Главная особенность панелей в том, что внутри каждой панели возможно использовать свой собственный компоновщик. \code{JPanel} -- это по умолчанию невидимый прямоугольник, на котором может находиться собственный компоновщик. Например, становится доступным создание для окна панели с кнопками, а остальное пространство оставить под другие важные вещи. В листинге \hrf{lst:jpanel-init} описан код создания панели, добавление её в нижнюю часть основного экрана, расположение внутри панели компоновщика и двух кнопок. Важно, что на экран добавляются не кнопки по отдельности, а компонент, на который предварительно добавили кнопки.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Создание и применение панели},label={lst:jpanel-init}]
GameWindow() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(WINDOW_POSX,WINDOW_POSY);
setSize(WINDOW_WIDTH,WINDOW_HEIGHT);
setTitle("TicTacToe");
setResizable(false);
JPanel panBottom = new JPanel(new GridLayout(1, 2));
panBottom.add(btnStart);
panBottom.add(btnExit);
add(panBottom, BorderLayout.SOUTH);
setVisible(true);
}
\end{lstlisting}
\code{JPanel} позволяет также осуществлять рисование и взаимодействие с пользователем. Основные графические интерактивности в демонстрационном приложении будут сделаны именно на панели. Такую панель с достаточно большой функциональностью логично выделить в отдельный класс. В случае игры в крестики-нолики это будет карта поля сражения (листинг \hrf{lst:jpanel-map}). В описании конструктора для простоты панель перекрашивается в чёрный цвет (строка \hrf{line:map-background}), чтобы увидеть, что панель создаётся без ошибок.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Создание панели с полем боя},label={lst:jpanel-map}]
package ru.gb.jdk.one.online;
import javax.swing.*;
import java.awt.*;
public class Map extends JPanel {
Map() {
setBackground(Color.BLACK); <@\label{line:map-background}@>
}
}
\end{lstlisting}
Естественно, панель также недостаточно просто создать (листинг \hrf{lst:jpanel-map-add}, строка \hrf{line:map-init}), но нужно её куда-то разместить. Например, на основной экран (строка \hrf{line:map-add}). Поскольку не была указана сторона экрана, панель заняла всё свободное место на окне, кроме юга, где расположилась панель с кнопками.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Добавление панели с полем боя},label={lst:jpanel-map-add}]
GameWindow() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(WINDOW_POSX, WINDOW_POSY);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setTitle("TicTacToe");
setResizable(false);
Map map = new Map(); <@\label{line:map-init}@>
JPanel panBottom = new JPanel(new GridLayout(1, 2));
panBottom.add(btnStart);
panBottom.add(btnExit);
add(panBottom, BorderLayout.SOUTH);
add(map); <@\label{line:map-add}@>
setVisible(true);
}
\end{lstlisting}
\subsection{Многооконное приложение, взаимосвязи}
\subsubsection{Структура}
Полученных знаний достаточно, чтобы начать описывать так называемую бизнес-логику. Созданная панель \code{Map} будет выполнять функции поля боя, поэтому логично расположить в ней метод startNewGame(), начинающий новую игру. В качестве параметров метод должен принимать какие-то начальные настройки самой игры. Например, будут два режима игры, компьютер против игрока и игрок против игрока, размер поля, и сразу не будем привязываться к квадратному полю 3х3, для полей больше, чем 3х3 понадобится выигрышная длина, то есть число крестиков или ноликов, расположенных подряд на одной прямой для победы той или иной стороны. В теле метода сразу будет установлена так называемая заглушка, чтобы знать, что метод вызывается и все параметры передаются верно.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Метод начала новой игры},label={lst:map-start-new-game}]
void startNewGame(int mode, int fSzX, int fSzY, int wLen) {
System.out.printf("Mode: %d;\nSize: x=%d, y=%d;\nWin Length: %d",
mode, fSzX, fSzY, wLen);
}
\end{lstlisting}
Если сразу описать архитектуру проекта, его будет проще наполнять логикой и расширять, чем если писать последовательно, удерживая общую картину в голове. Итоговое приложение приложение будет работать в двух окнах: первое -- стартовое, где будут задаваться настройки поля и производиться выбор режима игры; второе -- основное, где будет происходить собственно игра. Основное окно уже написано, и при его закрытии происходит выход из программы. Для создания второго окна необходимо написать ещё один класс, названный, например, \code{SettingsWindow}, наследник \code{JFrame}. Конструктор второго окна будет принимать экземпляр игрового окна. В первую очередь это сделано для передачи параметров игры, а во-вторых, чтобы красиво отцентрировать его относительно основного.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Заготовка окна настроек новой игры игры},label={lst:frame-settings}]
package ru.gb.jdk.one.online;
import javax.swing.*;
public class SettingsWindow extends JFrame {
SettingsWindow(GameWindow gameWindow) {
}
}
\end{lstlisting}
В основном окне \code{GameWindow} понадобится два поля, одно класса \code{SettingsWindow} чтобы иметь возможность экземпляр этого окна показывать когда появится необходимость и второе -- это панель \code{Map}. В основном окне при создании экземпляра окна настроек в него передаётся \code{this}.
\begin{frm} \info
Обратите внимание, на этот способ применять this, когда неоюходимо передать в метод ссылку на объект, который вызывает этот метод, фактически, основное окно передаёт себя.
\end{frm}
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Создание окна настроек в основном окне},label={lst:frame-create-settings}]
Map map;
SettingsWindow settings;
GameWindow() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(WINDOW_POSX, WINDOW_POSY);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setTitle("TicTacToe");
setResizable(false);
map = new Map();
settings = new SettingsWindow(this);
// ...
}
\end{lstlisting}
На рисунке \hrf{pic:ttt-class-diagram} первый черновик диаграммы классов разрабатываемого приложения\footnote{\href{https://www.plantuml.com/plantuml/uml/ZPBVQl904CNlzodckujV3GX5MbOhKhHggNyWGfHIojQiSMco2TbHIyK-UySqQJIbs5mCpEGt9pc7QHiK2Qx3WFt3bGmbn85Gch5588o1dWYbgxGNRQ7PlAl2TgLGjbgmOq2F3JlQHhNOmr9f4O3I2EvWr1cxp_tkeDUVmWtKw_Mpi3leJFi7jdPrbfsCdHcXrxNQNz0vePSP-W7tjsl4ICCBQkTW--Uu-wRowL3448guaRMEH5JQDraS9ciRB7jVH6LLs3uFCD_wFSIoikL_2ntf38NIj3qfRryKzZUHyY0apd8m8Rt79n29RogDOvMu959ujIgvqrGeFOkHt1viMM7aoIeidVTPMkSay23rbtBwPtQY_1NQn_V2GUbDz2eDj5WnvfmYXVyvJz_bdCe9aKSBaMsNmk7yj6TjgJqwtay0 }{Исходный код PlantUML}}. Создание идеальной диаграммы классов или модели данных не входит в цели курса, более важно понятное объяснение того, что в данный момент программируется. Буквами F обозначены экземпляры \code{JFrame}, буквой P \code{Jpanel}, а A это Application, то есть основной класс приложения. На диаграмме видно, что основное приложение создаёт основное окно, на которое добавлена панель \code{Map} и которое время от времени будет обращаться к \code{SettingsWindow} за настройками новой игры.
\begin{figure}[H]
\centering
\fontsize{7}{1}\selectfont
\includesvg[scale=.6]{pics/jd-01-ttt-01.svg}
\caption{Диаграмма классов приложения}
\label{pic:ttt-class-diagram}
\end{figure}
\subsubsection{Окно с настройками игры и обработчики кнопок}
Окно настроек игры на данный момент будет представлено одной кнопкой старта игры, вызывающей метод старта игры с одним зафиксированным набором настроек -- игра против компьютера, поле 3х3, чтобы выиграть необходимо собрать 3 крестика (или нолика) подряд.
В данный момент окно создаётся в координатах (0,0) и имеет размер (0,0), то есть в левом верхнем углу экрана видно только кнопки свернуть, развернуть, закрыть. В конструкторе окна задаются его размеры и то, что его местоположение должно быть относительным главному окну. Аналогично основному окну добавлена кнопка подтверждения правильности настроек и старта игры.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Окно настроек новой игры},label={lst:frame-settings-basic}]
public class SettingsWindow extends JFrame {
private static final int WINDOW_HEIGHT = 230;
private static final int WINDOW_WIDTH = 350;
JButton btnStart = new JButton("Start new game");
SettingsWindow(GameWindow gameWindow) {
setLocationRelativeTo(gameWindow);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
add(btnStart);
}
}
\end{lstlisting}
Далее необходимо «оживить» кнопки на окнах, это делается специальной конструкцией, синтаксис которой пока что придётся запомнить. Синтаксически всё написанное уже понятно и оговорено -- у объекта кнопки \code{btnExit} вызывается метод добавления к этому объекту некоторого слушателя действия. Какое может у кнопки быть самое очевидное действие? Нажатие. В аргумент метода добавления передаётся некий новый объект класса «слушатель действия», у которого переопределяется метод «действие произошло». Кнопка старта игры будет делать видимым окно с будущими настройками. В листинге \hrf{lst:main-btn-lsnr} показаны обработчики кнопок старта новой игры и завершения приложения, находящихся на основном окне программы. Эти обработчики необходимо поместить внутрь конструктора основного окна.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обработчики нажатий на кнопки основного окна},label={lst:main-btn-lsnr}]
btnExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
settings.setVisible(true);
}
});
\end{lstlisting}
\subsubsection{Последовательность выполнения программы}
В основном окне понадобится метод, инициализирующий новое игровое поле, поскольку прямой вызов по кнопке старта новой игры на окне настроек метода в панели основного окна противоречит инкапсуляции.
Зачем мы так делаем, казалось бы усложняем? Но нет: панель находится на основном окне, а кнопка начала игры будет находиться на окне настроек, которое не может «знать», какие на основном окне есть панели. Или может оказаться, что дальше нет никакого интерфейса, а игра происходит по сети.
\begin{frm} \excl
В этом и есть суть ООП, когда один объект максимально отделён от другого, и каждому из них вообще не важно, как реализован другой.
\end{frm}
Соответственно, когда в окне настроек нажата кнопка «начать игру», обработчик вызывает метод главного окна, а главное окно в свою очередь уже знает, что оно разделено на панели, и вынуждает панель \code{Map} начинать (рисунок \hrf{pic:starting-flow})\footnote{\href{www.plantuml.com/plantuml/uml/ZL1TQy9047o_Nx5zKp1WAzAYLaIqQcbLY125GhcQqnmab-1jQgdOtzwzSjQFizWy16vsPdPdMXhv2lCaPbSOYKH05dEf69l7N6leyKG4KeNf6XgDXnAi8ucYsOGD0_eys90QvNmB2wbu358X18DXPnIylFQxWrv_0lTGhLOliuD1Pz8tvFBjPV9uv4-9UrSk_uix8sx5Sh_WiPqZfWhUKFackWjtF-GEVUOP93ohswSl4ALQQbk9jiywi_DzNOMYXVIHn8yEHsRzKAoDYi2jBVrqYrkySqbX-Rlud7aRrNWbj1RXuHhAHjvZrvi6XU8kydigm-DBapGK9LZudzEV_umCdeGY0Jdl2wZLtEJWULxpD5uDhjaHHDBpnvCypyZ0eEUOP7N3_XnwAxcCq3Ff75c5jOGAyoJ-1W00 }{Исходный код диаграммы в PlantUML}}. Для чего нужен промежуточный метод? Чтобы не делать лишних связей между классами. Это логично с точки зрения инкапсуляции. Одно окно не должно никак управлять панелью на другом окне.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обработчик кнопки старта новой игры}]
SettingsWindow(GameWindow gameWindow) {
setLocationRelativeTo(gameWindow);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
gameWindow.startNewGame(0, 3, 3, 3);
setVisible(false);
}
});
add(btnStart);
}
\end{lstlisting}
В окне настроек описан обработчик нажатия на единственную кнопку, из этого обработчика вызывается единственный доступный метод -- «старт новой игры» на основном окне. По факту нажатия, также, целесообразно спрятать окно настроек. Из метода основного класса \code{startNewGame()} вызывается \code{map.startNewGame()} класса мэп.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Цепочка вызовов методов старта новой игры}]
void startNewGame(int mode, int fSzX, int fSzY, int wLen) {
map.startNewGame(mode, fSzX, fSzY, wLen);
}
\end{lstlisting}
Ещё раз цепочка вызовов (рис. \hrf{pic:starting-flow}):
\begin{enumerate}
\item основное окно делает окно настроек видимым;
\item окно настроек говорит основному, что пора начинать игру;
\item основное окно в свою очередь знает, как именно надо игру начинать и просит панель стартовать.
\end{enumerate}
Если всё сделано верно, в терминале появится вывод из заглушки на панели \code{Map}.
\begin{verbatim}
Mode: 0;
Size: x=3, y=3;
Win Length: 3
\end{verbatim}
\begin{figure}[H]
\centering
\fontsize{8}{1}\selectfont
\includesvg[scale=.7]{pics/jd-01-ttt-invoke.svg}
\caption{Цепочка вызовов при старте новой игры}
\label{pic:starting-flow}
\end{figure}
\subsubsection{Вопросы для самопроверки}
\begin{enumerate}
\item Менеджер размещений - это
\begin{enumerate}
\item сотрудник, занимающийся разработкой интерфейса;
\item объект, выполняющий расстановку компонентов на окне приложения;
\item механизм, проверяющий возможность отображения окна в ОС.
\end{enumerate}
\item Экземпляр JPanel позволяет
\begin{enumerate}
\item применять комбинации из компоновщиков
\item добавить к интерфейсу больше компонентов
\item создавать группы компонентов
\item всё вышеперечисленное
\end{enumerate}
\item Для выполнения кода по нажатию кнопки на интерфейсе нужно
\begin{enumerate}
\item создать обработчик кнопки и вписать код в него
\item переопределить метод нажатия у компонента кнопки
\item использовать специальный класс “слушателя” кнопок
\end{enumerate}
\end{enumerate}
\subsection{Основная панель с игрой}
\subsubsection{Рисование}
Далее всё будет происходить на панели с полем для игры. Для рисования самой панели фреймворком определён метод \code{paintComponent()}. Этот метод вызывается фреймворком когда что-то происходит, например, когда основное окно перекрывается другим, перемещается на другой экран, или если его развернуть из свёрнутого состояния, вызывается он гораздо реже, чем это необходимо для логики игры. Для описания игрового процесса необходимо перерисовывать компонент по каждому клику мышкой и по каждому действию оппонента.
\begin{frm} \excl
Важно помнить, что метод \code{paintComponent()} не следует напрямую вызывать из кода. Этот метод должен вызываться только фреймворком. Для того чтобы запросить у фреймворка вызов этого метода тоже есть специальный метод.
\end{frm}
Для дальнейшей разработки важно отделить стандартный метод рисования компонента от пользовательского рисования на этом компоненте, так называемую бизнес-логику. Для этого будет создан ещё один метод \code{void render(Graphics g)}, который будет вызываться из переопределённого \code{paintComponent()}. из самого \code{paintComponent()} вызов метода родительского класса удалять не следует, поскольку там, скорее всего, происходит что-то важное. Для вызова же метода фреймворка, необходимо в нужный момент сказать фреймворку что требуется перерисовать панель, фреймворк поставит метод \code{paintComponent()} в очередь сообщений окна, и когда очередь дойдёт до выполнения этого метода -- окно выполнит перерисовку.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Отделение бизнес-логики рисования}]
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
render(g);
}
private void render(Graphics g) { }
\end{lstlisting}
\begin{frm} \excl
Это действие полностью асинхронно и \textit{косвенно} зависит от наших вызовов
\end{frm}
Чтобы рисовать нужен объект класса \code{Graphics}, который умеет рисовать геометрические фигуры, линии, текст и тому подобное. Чтобы нарисовать поле для игры понадобится ширина и высота поля в пикселях. Их возможно узнать из свойств панели -- ширины и высоты. Всё, что связано с размерами лучше вынести в переменные объекта, поскольку они понадобятся в других методах. Помимо ширины и высоты понадобятся переменные, в которых будет храниться высота и ширина каждой ячейки. Размеры каждой ячейки пригодятся для создания отступа одной линии от другой. Далее циклически делаются отступы и рисуются горизонтальные и вертикальные линии.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Разлиновка поля для игры}]
private int panelWidth;
private int panelHeight;
private int cellHeight;
private int cellWidth;
private void render(Graphics g) {
panelWidth = getWidth();
panelHeight = getHeight();
cellHeight = panelHeight / 3;
cellWidth = panelWidth / 3;
g.setColor(Color.BLACK);
for (int h = 0; h < 3; h++) {
int y = h * cellHeight;
g.drawLine(0, y, panelWidth, y);
}
for (int w = 0; w < 3; w++) {
int x = w * cellHeight;
g.drawLine(x, 0, x, panelHeight);
}
repaint();
}
\end{lstlisting}
У многих разработчиков, в зависимости от используемой операционной системы после запуска этого кода не полностью или вовсе не рисуется разлиновка, это происходит из-за асинхронности рисования, скорее всего метод с линиями отработал позже того, как Swing нарисовал панель.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-01-ttt-lined-result.png}
\caption{Результат разлиновки поля для игры}
\end{figure}
Чтобы всё увидеть, необходимо заставить компонент панели полностью перерисоваться. Это делается вызовом метода \code{repaint()} из метода старта новой игры.
\subsubsection{Обработчик мышки}
Обработчик действий мышки очень похож на те обработчики, которые уже написаны. В конструкторе панели описывается метод добавления слушателя, в котором переопределяется метод \code{mouseReleased()}, то есть для приложения важно когда пользователь отпустит кнопку и аналогично методу отрисовки следует сразу отделить обработчик от основной исполняемой логики.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обработчик отпускания кнопки мышки}]
Map() {
addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
update(e);
}
});
}
\end{lstlisting}
Внутри метода обновления также принудительно вызывается метод перерисовки компонента, чтобы получился игровой цикл: старт -- отрисовка -- клик мыши -- отрисовка -- клик -- отрисовка...
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обработчик отпускания кнопки мышки}]
private void update(MouseEvent e) {
int cellX = e.getX() / cellWidth;
int cellY = e.getY() / cellHeight;
System.out.printf("x=%d, y=%d\n", cellX, cellY);
repaint();
}
\end{lstlisting}
В методе обновления из объекта \code{MouseEvent} получаются координаты клика, делятся на размер ячейки и тем самым получается номер ячейки, в которую произошёл клик.
\subsubsection{Логика игры}
Поскольку лекция про графические интерфейсы и ООП, а все используемые конструкции примитивны, код логики игры будет приведён без подробных пояснений.
Для работы понадобится генератор псевдослучайных чисел, символы, которыми будет обозначаться на поле игрок, компьютер и пустая ячейка, собственно поле и его размеры. Размеры -- на будущее.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Субъекты игры}]
private static final Random RANDOM = new Random();
private final int HUMAN_DOT = 1;
private final int AI_DOT = 2;
private final int EMPTY_DOT = 0;
private int fieldSizeY = 3;
private int fieldSizeX = 3;
private char[][] field;
\end{lstlisting}
Метод инициализации поля -- создаётся новый массив и заполняется пустыми символами. Его вызов логично сразу разместить в метод старта новой игры.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Инициализация карты}]
private void initMap() {
fieldSizeY = 3;
fieldSizeX = 3;
field = new char[fieldSizeY][fieldSizeX];
for (int i = 0; i < fieldSizeY; i++) {
for (int j = 0; j < fieldSizeX; j++) {
field[i][j] = EMPTY_DOT;
}
}
}
\end{lstlisting}
Когда кто-то (игрок или компьютер) будет совершать ход, будет важно, попал ли игрок в какую-то ячейку поля и пустота ли она, потому что нельзя ставить крестик поверх нолика и наоборот.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Попал ли игрок в пустую ячейку и в поле}]
private boolean isValidCell(int x, int y) {
return x >= 0 && x < fieldSizeX && y >= 0 && y < fieldSizeY;
}
private boolean isEmptyCell(int x, int y) {
return field[y][x] == EMPTY_DOT;
}
\end{lstlisting}
Компьютер будет очень примитивный -- он будет делать ход в случайные места на карте.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Ход компьютера}]
private void aiTurn() {
int x, y;
do {
x = RANDOM.nextInt(fieldSizeX);
y = RANDOM.nextInt(fieldSizeY);
} while (!isEmptyCell(x, y));
field[y][x] = AI_DOT;
}
\end{lstlisting}
Очевидно, что учебные цели предполагают не только демонстрацию того, как надо делать, но также и демонстрацию того как делать не надо. Далее приведён код, который не следует допускать при работе над приложениями. Метод принимает на вход символ, который нужно проверить и проверяет - не победил ли он.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Проверка победы одного из игроков}]
private boolean checkWin(char c) {
if (field[0][0]==c && field[0][1]==c && field[0][2]==c) return true;
if (field[1][0]==c && field[1][1]==c && field[1][2]==c) return true;
if (field[2][0]==c && field[2][1]==c && field[2][2]==c) return true;
if (field[0][0]==c && field[1][0]==c && field[2][0]==c) return true;
if (field[0][1]==c && field[1][1]==c && field[2][1]==c) return true;
if (field[0][2]==c && field[1][2]==c && field[2][2]==c) return true;
if (field[0][0]==c && field[1][1]==c && field[2][2]==c) return true;
if (field[0][2]==c && field[1][1]==c && field[2][0]==c) return true;
return false;
}
\end{lstlisting}
\begin{frm} \excl
Всегда пишите с помощью циклов, потому что стоит захотеть изменить размер поля на 4х4 или 5х5 -- размер и сложность этого метода будет расти в геометрической прогрессии.
\end{frm}
И метод проверки поля на состояние ничьей. Ничья в крестиках-ноликах наступает, когда не победил ни игрок ни оппонент, и не осталось пустых клеток.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Проверка на ничью}]
private boolean isMapFull() {
for (int i = 0; i < fieldSizeY; i++) {
for (int j = 0; j < fieldSizeX; j++) {
if (field[i][j] == EMPTY_DOT) return false;
}
}
return true;
}
\end{lstlisting}
Вся дальнейшая работа будет сконцентрирована на методах обновления игрового состояния и отрисовки игрового поля. В результате клика в ячейку необходимо проверить, валидная-ли ячейка, и можно-ли туда ходить. Если какое-то из условий не прошло, клик игнорируется, а если всё хорошо -- делается ход.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Ход игрока}]
int cellX = e.getX()/cellWidth;
int cellY = e.getY()/cellHeight;
if (!isValidCell(cellX, cellY) || !isEmptyCell(cellX, cellY)) return;
field[cellY][cellX] = HUMAN_DOT;
repaint();
\end{lstlisting}
В метод отрисовки также необходимо добавить логику. Если в ячейке поля ничего нет -- ничего делать не нужно, далее условие, если в ячейке крестик -- что-то сделаем, если нолик -- сделаем что-то другое и в противном случае выбросим исключение.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Отрисовка поля}]
for (int y = 0; y < fieldSizeY; y++) {
for (int x = 0; x < fieldSizeX; x++) {
if (field[y][x] == EMPTY_DOT) continue;
if (field[y][x] == HUMAN_DOT) {
g.setColor(Color.BLUE);
g.fillOval(x * cellWidth + DOT_PADDING,
y * cellHeight + DOT_PADDING,
cellWidth - DOT_PADDING * 2,
cellHeight - DOT_PADDING * 2);
} else if (field[y][x] == AI_DOT) {
g.setColor(new Color(0xff0000));
g.fillOval(x * cellWidth + DOT_PADDING,
y * cellHeight + DOT_PADDING,
cellWidth - DOT_PADDING * 2,
cellHeight - DOT_PADDING * 2);
} else {
throw new RuntimeException("Unexpected value " + field[y][x] +
" in cell: x=" + x + " y=" + y);
}
}
}
\end{lstlisting}
Далее -- непосредственно отрисовка. Сюда можно картинку вставлять, закрашивать квадраты, рисовать крестики и нолики. Для простоты будут рисоваться кружки. Методу объекта графики \code{g.fillOval()} в сигнатуре передаётся левая верхняя координата прямоугольника, в который затем будет вписан овал, его ширина и высота соответственно. Чтобы задать цвет -- перед тем как рисовать необходимо изменить цвет объекта графики \code{g.setColor(Color.BLUE)}. Для человека далее будут рисоваться синие кружки, а для компьютера красные.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-01-ttt-hum-turn.png}
\end{figure}
\subsubsection{Последние приготовления}
По сути, осталось сделать две вещи -- описать так называемую бизнес-логику, то есть в правильном порядке вызвать методы с логикой игры, избавиться от исключений и вывести сообщение об окончании игры. Для того, чтобы вывести результат, поверх игрового поля будет выводиться сообщение.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Состояния игрового поля}]
private int gameOverType;
private static final int STATE_DRAW = 0;
private static final int STATE_WIN_HUMAN = 1;
private static final int STATE_WIN_AI = 2;
private static final String MSG_WIN_HUMAN = "Победил игрок!";
private static final String MSG_WIN_AI = "Победил компьютер!";
private static final String MSG_DRAW = "Ничья!";
\end{lstlisting}
В методе обновления уточняется, что когда пользователь поставил точку, необходимо проверить состояние поля на наличие победы или ничьей, дать возможность компьютеру поставить точку и сделать тоже самое.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Логика обновления}]
// update
if (checkEndGame(HUMAN_DOT, STATE_WIN_HUMAN)) return;
aiTurn();
repaint();
if (checkEndGame(AI_DOT, STATE_WIN_AI)) return;
// end update
private boolean checkEndGame(int dot, int gameOverType) {
if (checkWin(dot)) {
this.gameOverType = gameOverType;
repaint();
return true;
}
if (isMapFull()) {
this.gameOverType = STATE_DRAW;
repaint();
return true;
}
return false;
}
\end{lstlisting}
В методе рендеринга, как только поле выведено и если игра закончилась необходимо вывести сообщение с одним из вариантов исхода игры. Для упрощения также следует завести классовую переменную с признаком окончания игры. Метод окончания игры рисует тёмно серый прямоугольник с жёлтой надписью о победе одного из игроков или ничьей в зависимости от состояния.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Отрисовка сообщения об окончании игры}]
//render
if (isGameOver) showMessageGameOver(g);
// end render
private void showMessageGameOver(Graphics g) {
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 200, getWidth(), 70);
g.setColor(Color.YELLOW);
g.setFont(new Font("Times new roman", Font.BOLD, 48));
switch (gameOverType) {
case STATE_DRAW:
g.drawString(MSG_DRAW, 180, getHeight() / 2); break;
case STATE_WIN_AI:
g.drawString(MSG_WIN_AI, 20, getHeight() / 2); break;
case STATE_WIN_HUMAN:
g.drawString(MSG_WIN_HUMAN, 70, getHeight() / 2); break;
default:
throw new RuntimeException("Unexpected gameOver state: " + gameOverType);
}
}
\end{lstlisting}
Далее в листинге \hrf{lst:minor-additions} приведены несколько мелких правок в методах. Прочитав текст исключения при старте приложения становится ясно, что оно возникает, когда программа не может что-то поделить на ноль. Размеры поля до их инициализации равны нулю, поэтому понадобится ещё одна булева переменная -- инициализирована ли игра.
\begin{itemize}
\item В конструкторе панели поле не инициализировано;
\item в методе обновления нет смысла обрабатывать клики по неинициализированному полю или полю на котором закончилась игра;
\item при старте новой игры игра перестаёт быть законченой, а поле становится инициализированным;
\item рендеринг на неинициализированном поле не имеет смысла;
\item в методе проверки на победу нужно добавить присвоение истинности булевой переменной с фактом окончания игры.
\end{itemize}
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Незначительные добавления в методах панели},label={lst:minor-additions}]
private boolean isGameOver;
private boolean isInitialized;
Map() {
isInitialized = false;
}
private void update(MouseEvent e) {
if (isGameOver || !isInitialized) return;
}
void startNewGame(int mode, int fSzX, int fSzY, int wLen) {
isGameOver = false;
isInitialized = true;
}
private void render(Graphics g) {
if (!isInitialized) return;
}
private boolean checkEndGame(int dot, int gameOverType) {
if (checkWin(dot)) {
isGameOver = true;
}
if (isMapFull()) {
isGameOver = true;
}
return false;
}
\end{lstlisting}
Результат запуска получившегося приложения представлен на рисунке \hrf{pic:ttt-results}.
\begin{figure}[H]
\centering
\begin{subfigure}[b]{0.3\textwidth}
\centering
\includegraphics[width=\textwidth]{jd-01-ttt-rslt-cmp.png}
\caption{Победа компьютера}
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.3\textwidth}
\centering
\includegraphics[width=\textwidth]{jd-01-ttt-rslt-hum.png}
\caption{Победа игрока}
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.3\textwidth}
\centering
\includegraphics[width=\textwidth]{jd-01-ttt-rslt-drw.png}
\caption{Ничья}
\end{subfigure}
\caption{Результаты игры}
\label{pic:ttt-results}
\end{figure}
\subsection*{Практическое задание}
\begin{enumerate}
\item Полностью разобраться с кодом.
\item Переделать проверку победы, чтобы она не была реализована просто набором условий.
\item Попробовать переписать логику проверки победы, чтобы она работала для поля 5х5 и количества фигур 4.
\item ** Доработать искусственный интеллект, чтобы он мог примитивно блокировать ходы игрока, и примитивно пытаться выиграть сам.
\end{enumerate}
\newpage
\printnomenclature[40mm]
\end{document}

View File

@ -2,11 +2,10 @@
\usepackage{spreadtab}
\begin{document}
%\setcounter{section}{4}
\section{Семинар: Простейшие интерфейсы пользователя}
\subsection{Инструментарий}
\begin{itemize}
\item \href{http://}{Презентация} для преподавателя, ведущего семинар;
\item \href{https://docs.google.com/presentation/d/1ji8J8XfgjWyAmoIkQyHOyoWGsnAbD3Sk7-D7YBlaQIM}{Презентация} для преподавателя, ведущего семинар;
\item \href{https://drive.google.com/file/d/1LWyE8aEy4-1gsognqhXIXwDcoLviVge4/view}{Фон} GeekBrains для проведения семинара в Zoom;
\item JDK любая 11 версии и выше;
\item \href{https://www.jetbrains.com/idea/download}{IntelliJ IDEA Community Edition} для практики и примеров используется IDEA.
@ -31,25 +30,25 @@
\hline
@ Quiz & 5 & @ 6-18 & @ Преподаватель задаёт вопросы викторины, через 30 секунд демонстрирует слайд-подсказку и ожидает ответов (по минуте на ответ) \\
\hline
@ Рассмотрение ДЗ лекции & 10 & @ 19-27 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
@ Рассмотрение ДЗ лекции & 15-20 & @ 19-23 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
\hline
@ Вопросы и ответы & 10 & @ 28 & @ Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы \\
@ Вопросы и ответы & 10 & @ 24 & @ Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы \\
\hline
@ Задание 1 & 10 & @ 29-33 & @ \\
@ Задание 1 & 15 & @ 25-30 & @ Создание и компоновка элементов управления \\
\hline
@ Задание 2 & 15 & @ 34-37 & @ \\
@ Задание 2 & 15 & @ 31-35 & @ Связь компонентов и сбор сведений о состоянии компонента \\
\hline
@ Перерыв (если нужен) & 5 & @ 38 & @ Преподаватель предлагает студентам перерыв на 5 минут (студенты голосуют) \\
@ Задание 3 & 10 & @ 36-38 & @ Передача данных с компонентов \\
\hline
@ Задание 3 & 10 & @ 39-42 & @ \\
@ Перерыв (если нужен) & 5 & @ 39 & @ Преподаватель предлагает студентам перерыв на 5 минут (студенты голосуют) \\
\hline
@ Задание 4 & 15 & @ 43-46 & @ \\
@ Задание 4 & 15 & @ 40-44 & @ Создание окна управления сервером \\
\hline
@ Задание 5 & 20 & @ 47-49 & @ \\
@ Задание 5 & 30 & @ 45-48 & @ Создание окна клиентской части текстового чата \\
\hline
@ Домашнее задание & 5 & @ 50-51 & @ Объясните домашнее задание, подведите итоги урока \\
@ Домашнее задание & 5 & @ 49-50 & @ Объясните домашнее задание, подведите итоги урока \\
\hline
@ Рефлексия & 10 tag(end) & @ 52-53 & @ Преподаватель запрашивает обратную связь \\
@ Рефлексия & 10 tag(end) & @ 51-52 & @ Преподаватель запрашивает обратную связь \\
\hline
@ Длительность & sum(cell(beg):cell(end)) & & \\
\hline
@ -488,7 +487,7 @@ Settings(GameWindow mainWindow) {
\begin{lstlisting}[language=Java,style=JCodeStyle]
public class Map extends JPanel {
public static final int MODE_HVA = 0;
public static final int MODE_HVH = 0;
public static final int MODE_HVH = 1;
//...
}
@ -515,7 +514,7 @@ private void btnStartDelegate() {
\subsubsection*{Задание 4}
\begin{itemize}
\item \textbf{Ценность этапа} Создание окна управления сервером
\item \textbf{Тайминг} 15-20 мин
\item \textbf{Тайминг} 10-15 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
@ -670,7 +669,7 @@ private ServerWindow() {
\subsubsection*{Задание 5}
\begin{itemize}
\item \textbf{Ценность этапа} Создание окна клиентской части текстового чата
\item \textbf{Тайминг} 25-30 мин
\item \textbf{Тайминг} 20-25 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;

1028
jtd2-07-abstract.tex Normal file

File diff suppressed because it is too large Load Diff

600
jtd2-07-workshop.tex Normal file
View File

@ -0,0 +1,600 @@
\documentclass[j-spec.tex]{subfiles}
\usepackage{spreadtab}
\begin{document}
\section{Семинар: Интерфейсы и API}
\subsection{Инструментарий}
\begin{itemize}
\item \href{https://docs.google.com/presentation/d/1OBDGtC5dJz7Tu9zEJDayETOKZL0aPXne3Y8VUOGZUUU/edit?usp=share_link}{Презентация} для преподавателя, ведущего семинар;
\item \href{https://drive.google.com/file/d/1LWyE8aEy4-1gsognqhXIXwDcoLviVge4/view}{Фон} GeekBrains для проведения семинара в Zoom;
\item JDK любая 11 версии и выше;
\item \href{https://www.jetbrains.com/idea/download}{IntelliJ IDEA Community Edition} для практики и примеров используется IDEA.
\end{itemize}
\subsection{Цели семинара}
\begin{itemize}
\item Практика создания и проектирования интерфейсов;
\item передача и обработка сообщений с использованием интерфейсов;
\item Применение существующих интерфейсов для обработки исключений;
\item Отделение графического, сетевого и логического слоёв для приложения «Сетевой чат».
\end{itemize}
\subsection{План-содержание}
\noindent
\begin{spreadtab}{{longtable}{|p{37mm}|l|l|p{90mm}|}}
\hline
@ Что происходит & @ Время & @ Слайды & @ Описание \\
\hline
\endhead
@ Организационный момент & 5 tag(beg) & @ 1-5 & @ Преподаватель ожидает студентов, поддерживает активность и коммуникацию в чате, озвучивает цели и планы на семинар. Важно упомянуть, что выполнение домашних заданий с лекции является, фактически, подготовкой к семинару \\
\hline
@ Quiz & 5 & @ 6-18 & @ Преподаватель задаёт вопросы викторины, через 30 секунд демонстрирует слайд-подсказку и ожидает ответов (по минуте на ответ) \\
\hline
@ Рассмотрение ДЗ лекции & 10 & @ 19-24 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
\hline
@ Вопросы и ответы & 10 & @ 25 & @ Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы \\
\hline
@ Задание 1 & 30 & @ 26-30 & @ Отделение бизнес-логики и графического интерфейса \\
\hline
@ Перерыв (если нужен) & 5 & @ 31 & @ Преподаватель предлагает студентам перерыв на 5 минут (студенты голосуют) \\
\hline
@ Задание 2 & 30 & @ 32-36 & @ Создание и частичная реализация интерфейсов для улучшения понимания механизмов взаимодействия объектов \\
\hline
@ Задание 3 & 10 & @ 37-40 & @ Написать абстрактный пример применения интерфейсов с реализацией по умолчанию и наследование \\
\hline
@ Домашнее задание & 5 & @ 41-42 & @ Объясните домашнее задание, подведите итоги урока \\
\hline
@ Рефлексия & 10 tag(end) & @ 43-44 & @ Преподаватель запрашивает обратную связь \\
\hline
@ Длительность & sum(cell(beg):cell(end)) & & \\
\hline
\end{spreadtab}
\subsection{Подробности}
\subsubsection*{Организационный момент}
\begin{itemize}
\item \textbf{Цель этапа:} Позитивно начать урок, создать комфортную среду для обучения.
\item \textbf{Тайминг:} 3-5 минут.
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Запрашивает активность от аудитории в чате;
\item Презентует цели курса и семинара;
\item Презентует краткий план семинара и что студент научится делать.
\end{itemize}
\end{itemize}
\subsubsection*{Quiz}
\begin{itemize}
\item \textbf{Цель этапа:} Вовлечение аудитории в обратную связь.
\item \textbf{Тайминг:} 5--7 минут (4 вопроса, по минуте на ответ).
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель задаёт вопросы викторины, представленные на слайдах презентации;
\item через 30 секунд демонстрирует слайд-подсказку и ожидает ответов.
\end{itemize}
\item \textbf{Вопросы и ответы:}
\begin{enumerate}
\item Множественное наследование в Java (3)
\begin{enumerate}
\item запрещено;
\item разрешено;
\item разрешено для интерфейсов.
\end{enumerate}
\item Интерфейсы позволяют (2)
\begin{enumerate}
\item удобно создавать новые объекты, не связанные наследованием;
\item единообразно обращаться к методам объектов, не связанных наследованием;
\item полностью заменить наследование.
\end{enumerate}
\item Поле в интерфейсе (2)
\begin{enumerate}
\item невозможно;
\item public static final;
\item private final.
\end{enumerate}
\item Обработчик исключений для графического потока (2)
\begin{enumerate}
\item это статический метод;
\item это интерфейс;
\item реализован JVM.
\end{enumerate}
\end{enumerate}
\end{itemize}
\subsubsection*{Рассмотрение ДЗ}
\begin{itemize}
\item \textbf{Цель этапа:} Пояснить не очевидные моменты в формулировке ДЗ с лекции, синхронизировать прочитанный на лекции материал к началу семинара.
\item \textbf{Тайминг:} 15-20 минут.
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель демонстрирует свой вариант решения домашнего задания из лекции;
\item возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов.
\end{itemize}
\item \textbf{Домашнее задание из лекции:}
\begin{itemize}
\item Полностью разобраться с кодом -- это задание не нужно обсуждать, возможно просто спросить, кто действительно сел, взял в руки карандаш и блокнот, и попытался разобраться с кодом с лекции. Похвалить тех, кто это действительно сделал.
\item Для приложения с шариками описать появление и убирание шариков по клику мышки левой и правой кнопкой соответственно
\textbf{Вариант решения}
На лекции был описан инициализатор приложения, заполняющий массив игровых объектов шариками. необходимо было описать слушатель мышки, добавляющий и убирающий шарики из массива
\begin{lstlisting}[language=Java,style=JCodeStyle]
if (e.getButton() == MouseEvent.BUTTON1) {
interactables[objectsCount++] = new Ball(e.getX(), e.getY());
} else if (e.getButton() == MouseEvent.BUTTON3) {
if (objectsCount == 1) return;
objectsCount--;
}
\end{lstlisting}
Для обеспечения работоспособности данного кода необходимо добавить в мячик конструктор, принимающий начальные координаты
\begin{lstlisting}[language=Java,style=JCodeStyle]
Ball(int x, int y) {
this();
this.x = x;
this.y = y;
}
\end{lstlisting}
\item Написать, выбросить и обработать такое исключение, которое не позволит создавать более, чем 15 шариков.
\textbf{Вариант решения}
Очевидно, что обработка исключений должна вызвать ряд проблем, поскольку тема обработки исключений при использовании графических интерфейсов рассматривается только на следующей лекции, но за попытку решить проблему следует похвалить студентов.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Собственно, исключение}]
public class BallsOverflowException extends RuntimeException {
BallsOverflowException() {
super("Balls overflow, 15 allowed!");
}
}
\end{lstlisting}
По очевидным причинам выбрасывать исключение там, где добавляются шарики -- не логично, поэтому необходимо выбрасывать или на апдейте или на рендере.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Место выброса исключения}]
private void update(MainCanvas canvas, float deltaTime) {
for (int i = 0; i < objectsCount; i++) {
interactables[i].update(canvas, deltaTime);
}
if (objectsCount >= 15)
throw new BallsOverflowException();
}
\end{lstlisting}
Для обработки исключений в Swing необходимо установить обработчик исключений для потока. сделаем этим обработчиком «себя».
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обработка исключения}]
private MainWindow() { //constructor
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
if (e.getClass().equals(BallsOverflowException.class)) {
System.out.println(e.getMessage());
}
}
\end{lstlisting}
\item ** Написать ещё одно приложение, в котором на белом фоне будут перемещаться изображения формата png, лежащие в папке проекта.
\textbf{Вариант решения}
Главным отличием приложения будет работа с изображением. Такова была идея примера -- тиражируемость приложения. То есть вообще весь код может быть взят из шариков или квадратиков, но в главном рисуемом объекте должен происходить не рендеринг примитива, а рендеринг картинки.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={}]
public class Ball extends Sprite {
private Image img = null;
private float vX;
private float vY;
Ball() {
halfHeight = 64; // logo size magic numbers
halfWidth = 62;
try {
img = ImageIO.read(new File("logo.png"));
} catch (IOException e) {
throw new RuntimeException(e);
}
vX = 100f + (float) (Math.random() * 200f);
vY = 100f + (float) (Math.random() * 200f);
}
@Override
public void render(MainCanvas canvas, Graphics g) {
g.drawImage(img, (int) getLeft(), (int) getTop(), null);
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Вопросы и ответы}
\begin{itemize}
\item \textbf{Ценность этапа} Вовлечение аудитории в обратную связь, пояснение неочевидных моментов в материале лекции и другой проделанной работе.
\item \textbf{Тайминг} 5-15 минут
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы;
\item Если преподаватель затрудняется с ответом, необходимо мягко предложить студенту ответить на его вопрос на следующем семинаре (и не забыть найти ответ на вопрос студента!);
\item Предложить и показать пути самостоятельного поиска студентом ответа на заданный вопрос;
\item Посоветовать литературу на тему заданного вопроса;
\item Дополнительно указать на то, что все сведения для выполнения домашнего задания, прохождения викторины и работы на семинаре были рассмотрены в методическом материале к этому или предыдущим урокам.
\end{itemize}
\end{itemize}
\subsubsection*{Задание 1}
\begin{itemize}
\item \textbf{Ценность этапа} Отделение бизнес-логики и графического интерфейса.
\item \textbf{Тайминг} 25-30 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Пояснить студентам пользу и необходимость следования паттерну MVC, проговорить важность разделения логики и открывающиеся возможности при правильном проектировании.
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item На предыдущем семинаре было описано окно сервера приложения, содержащее две кнопки (старт и стоп) и текстовое поле журнала. Необходимо вынести логику работы сервера в класс \code{ChatServer}, а в обработчиках кнопок оставить только логику нажатия кнопки и журналирования сообщений от сервера.
Для достижения цели необходимо описать интерфейс «слушатель сервера», с методом «получить сообщение», вызывать его с одной стороны, и реализовать с другой.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
//ChatServerListener.java
package ru.gb.jdk.two.sem;
public interface ChatServerListener {
void onMessageReceived(String msg);
}
//ChatServer.java
package ru.gb.jdk.two.sem;
public class ChatServer {
private boolean isServerWorking;
private final ChatServerListener listener;
ChatServer(ChatServerListener listener) {
isServerWorking = false;
this.listener = listener;
}
public void start() {
if (isServerWorking) {
listener.onMessageReceived("Server is already working");
return;
}
listener.onMessageReceived("Server started");
isServerWorking = true;
}
public void stop() {
if (!isServerWorking) {
listener.onMessageReceived("Server is stopped");
return;
}
listener.onMessageReceived("Server stopped");
isServerWorking = false;
}
}
//ServerWindow.java
//constructor
server = new ChatServer(this);
btnStop.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
server.stop();
}
});
btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
server.start();
}
});
// method implementation
@Override
public void onMessageReceived(String msg) {
log.append(msg + "\n");
}
\end{lstlisting}
\item [$*_1$] Отделить функционал логгирования сообщений сервера для простоты изменения способа оповещения пользователя.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
@Override
public void onMessageReceived(String msg) {
putMessageToLog(msg);
}
private void putMessageToLog(String msg) {
log.append(msg + "\n");
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 2}
\begin{itemize}
\item \textbf{Ценность этапа} Создание и частичная реализация интерфейсов для улучшения понимания механизмов взаимодействия объектов.
\item \textbf{Тайминг} 25-30 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Создать интерфейсы \code{ServerSocketThreadListener} и \code{SocketThreadListener}, содержащие методы, соответствующие событиям сервера и клиента чата. Реализовать созданные интерфейсы простым логированием. Со стороны клиента -- только \code{SocketThreadListener}, со стороны сервера -- оба интерфейса.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.network;
import java.net.Socket;
public interface SocketThreadListener {
void onSocketStart(Socket s);
void onSocketStop();
void onSocketReady(Socket socket);
void onReceiveString(Socket s, String msg);
void onSocketException(Throwable e);
}
package ru.gb.jdk.two.sem.network;
import java.net.ServerSocket;
import java.net.Socket;
public interface ServerSocketThreadListener {
void onServerStart();
void onServerStop();
void onServerSocketCreated(ServerSocket s);
void onServerSoTimeout(ServerSocket s);
void onSocketAccepted(ServerSocket s, Socket client);
void onServerException(Throwable e);
}
\end{lstlisting}
\begin{lstlisting}[language=Java,style=JCodeStyle]
// Server
/**
* Server socket thread methods
* */
@Override
public void onServerStart() { listener.onMessageReceived("Server thread started"); }
@Override
public void onServerStop() { listener.onMessageReceived("Server thread stopped"); }
@Override
public void onServerSocketCreated(ServerSocket s) { listener.onMessageReceived("Server socket created"); }
@Override
public void onServerSoTimeout(ServerSocket s) {
// listener.onMessageReceived("Accept timeout");
}
@Override
public void onSocketAccepted(ServerSocket s, Socket client) { listener.onMessageReceived("client connected"); }
@Override
public void onServerException(Throwable e) { e.printStackTrace(); }
/**
* Socket Thread listening
* */
@Override
public synchronized void onSocketStart(Socket s) { listener.onMessageReceived("Client connected"); }
@Override
public synchronized void onSocketStop() { listener.onMessageReceived("Client dropped"); }
@Override
public synchronized void onSocketReady(Socket socket) { listener.onMessageReceived("Client is ready"); }
@Override
public synchronized void onReceiveString(Socket s, String msg) { listener.onMessageReceived(msg); }
@Override
public void onSocketException(Throwable e) { e.printStackTrace(); }
// Client
@Override
public void onSocketStart(Socket s) { log.append("Started" + "\n"); }
@Override
public void onSocketStop() { log.append("Stopped" + "\n"); }
@Override
public void onSocketReady(Socket socket) { log.append("Ready" + "\n"); }
@Override
public void onReceiveString(Socket s, String msg) { log.append(msg + "\n"); }
@Override
public void onSocketException(Throwable e) { e.printStackTrace(); }
\end{lstlisting}
\item [$*_1$] Создать классы -- \code{ServerSocketThread} и \code{SocketThread}, соответственно слушателям, то есть реализовать в классе конструкторы с возможностью сохранения ссылки на слушателей. Создать экземпляр \code{ServerSocketThread} из объекта \code{ChatServer}, а \code{SocketThread} из \code{ClientGUI}. На данном этапе, не важно, где именно будут создаваться эти объекты.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.network;
public class ServerSocketThread {
private ServerSocketThreadListener listener;
public ServerSocketThread(ServerSocketThreadListener listener) {
this.listener = listener;
}
}
package ru.gb.jdk.two.sem.network;
public class SocketThread {
private final SocketThreadListener listener;
public SocketThread(SocketThreadListener listener) {
this.listener = listener;
}
}
// Server
private ServerSocketThread server;
public void start() {
if (isServerWorking) {
listener.onMessageReceived("Server is already working");
return;
}
server = new ServerSocketThread(this);
listener.onMessageReceived("Server started");
isServerWorking = true;
}
//Client
private void connect() {
SocketThread socketThread = new SocketThread(this);
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 3}
\begin{itemize}
\item \textbf{Ценность этапа} Написать абстрактный пример применения интерфейсов с реализацией по умолчанию и наследованием.
\item \textbf{Тайминг} 10-15 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Описать команду разработчиков. В команде разработчиков могут находиться бэкендеры, которые в состоянии писать серверный код, фронтендеры, которые могут программировать экранные формы, и фуллстэк разработчики, совмещающие в себе обе компетенции. Реализовать класс фулстэк разработчика, создать экземпляр и последовательно вызвать все его методы.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.devs;
public interface Backender { void developServer(); }
public interface Frontender { void developGUI(); }
public interface Fullstack extends Backender, Frontender {
}
public class FullstackDeveloper implements Fullstack {
@Override
public void developServer() { System.out.println("Server done"); }
@Override
public void developGUI() { System.out.println("GUI done"); }
}
public class Main {
public static void main(String[] args) {
FullstackDeveloper dev = new FullstackDeveloper();
dev.developGUI();
dev.developServer();
}
}
\end{lstlisting}
\item [$*_1$] Добавить возможность при создании экземпляров классов любых интерфейсов -- добавлять их в один массив, например, \code{Developer[] team = {...};}
\textbf{Вариант решения}
К первому решению добавляется маркерный интерфейс «Разработчик». Очевидно, что нельзя для этого интерфейса вызвать более точные методы фуллстэк разработчика.
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.devs;
public interface Developer {
}
public interface Backender extends Developer { void developServer(); }
public interface Frontender extends Developer { void developGUI(); }
public class Main {
public static void main(String[] args) {
Developer dev = new FullstackDeveloper();
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Домашнее задание}
\begin{itemize}
\item \textbf{Ценность этапа} Задать задание для самостоятельного выполнения между занятиями. В данном семинаре домашнее задание не предусмотрено в связи со сложностью заданий семинара. Необходимо досконально разобраться в написанном (и запланированном коде) и взаимосвязях объектов. Также возможно уточнить, что задания семинара направлены на написание многопоточного сетевого чата, но, поскольку это будет достаточно сложный проект, его написание приводится постепенно
\item \textbf{Тайминг} 5-10 минут.
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Пояснить студентам в каком виде выполнять и сдавать задания
\item Уточнить кто будет проверять работы (преподаватель или ревьювер)
\item Объяснить к кому обращаться за помощью и где искать подсказки
\item Объяснить где взять проект заготовки для дз
\end{itemize}
\item \textbf{Задания}
\begin{enumerate}
\item [5-25 мин] Выполнить все задания семинара, если они не были решены, без ограничений по времени;
\textbf{Все варианты решения приведены в тексте семинара выше}
\item [5 мин] 1. Дописать третье задание таким образом, чтобы в идентификатор типа \code{Developer} записывался объект \code{Frontender}, и далее вызывался метод \code{developGUI()}, не изменяя существующие интерфейсы, только код основного класса.
\begin{lstlisting}[language=Java,style=JCodeStyle]
public class Main {
public static void main(String[] args) {
Developer dev = new FrontendDeveloper();
if (dev instanceof Frontender) {
((Frontender) dev).developGUI();
}
}
}
\end{lstlisting}
\end{enumerate}
\end{itemize}
\subsubsection*{Рефлексия и завершение семинара}
\begin{itemize}
\item \textbf{Цель этапа:} Привести урок к логическому завершению, посмотреть что студентам удалось, что было сложно и над чем нужно еще поработать
\item \textbf{Тайминг:} 5-10 минут
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Запросить обратную связь от студентов.
\item Подчеркните то, чему студенты научились на занятии.
\item Дайте рекомендации по решению заданий, если в этом есть необходимость
\item Дайте краткую обратную связь студентам.
\item Поделитесь ощущением от семинара.
\item Поблагодарите за проделанную работу.
\end{itemize}
\end{itemize}
\newpage
\end{document}

911
jtd3-08-abstract.tex Normal file
View File

@ -0,0 +1,911 @@
\documentclass[j-spec.tex]{subfiles}
\begin{document} \sloppy
\setcounter{section}{7}
\setlength{\columnsep}{22pt}
\pagestyle{plain}
\tableofcontents
\section{Инструментарий: Обобщения}
\subsection*{В предыдущем разделе}
\begin{itemize}
\item программные интерфейсы — понятие и принцип работы;
\item ключевое слово implements;
\item Наследование и множественное наследование интерфейсов;
\item реализация, реализации по-умолчанию;
\item частичная реализация интерфейсов, адаптеры;
\item анонимные классы.
\end{itemize}
\subsection*{В этом разделе}
Смысл обобщений в программировании примерно такой же, как и в обычной разговорной речи -- отсутствие деталей реализации. С точки зрения проектирования обобщения -- это более сложный полиморфизм. Будет рассмотрены diamond operator, обобщённые методы и подстановочные символы (Wildcards). Применение обобщений для ограничений типов сверху и снизу. Выведение и стирание типов, целевые типы и загрязнение кучи.
\begin{itemize}
\item \nom{Обобщения}{особый подход к описанию данных и алгоритмов, позволяющий работать с различными типами данных без изменения внешнего описания.};
\item \nom{Wildcard}{в обобщённом коде знак вопроса, называемый подстановочным символом, означает неизвестный тип. Подстановочный символ может использоваться в разных ситуациях: как параметр типа, поля, локальной переменной, иногда в качестве возвращаемого типа.};
\item \nom{Diamond operation}{автоматизация подстановки типа, пустые угловые скобки, вынуждающие компилятор вывести тип из контекста.};
\item \nom{Стирание типа}{подготовка написанного обобщённого кода к компиляции таким образом, чтобы в байт-коде не было обобщённых конструкций.};
\item \nom{Выведение типа}{это возможность компилятора автоматически определять аргументы типа на основе контекста};
\item \nom{Сырой (raw) тип}{это имя обобщённого класса или интерфейса без аргументов типа, то есть это, фактически, написание идентификатора и вызов конструктора обобщённого класса как обычного, без треугольных скобок.};
\item \nom{Целевой тип}{это тип данных, который компилятор Java ожидает в зависимости от того, в каком месте находится выражение.}.
\end{itemize}
\subsection{Понятие обобщения}
\subsubsection{Работа без обобщений}
Обобщения -- это механизм создания общего подхода к реализации одинаковых алгоритмов, но с разными данными. Обобщения -- это некоторые конструкции, позволяющие работать с данными не задумываясь о том, какие именно данные лежат внутри структуры или метода (строки, числа или коты). Обобщения позволяют единообразно работать с разными типами данных.
\begin{frm} \info
Обобщённое программирование в Java позволяет создавать классы, интерфейсы и методы, которые могут работать с различными типами данных. В результате, код становится более универсальным и повторно используемым.
\end{frm}
Для приведения примера поведения контейнера, который может хранить данные любого типа необходимо создать некоторый класс, который будет хранить внутри \code{Object} (любые данные Java). На примере чисел, в основном методе приложения созданы два экземпляра коробок с числами. Числа, которые сохранены в коробке желательно иметь возможность не только хранить, но и использовать, например, производить операцию сложения.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Контейнер, хранящий любые данные}]
private static class Box {
private Object obj;
public Box(Object obj) {
this.obj = obj;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public void printInfo() {
System.out.printf("Box (%s): %s\n",
obj.getClass().getSimpleName(),
obj.toString());
}
}
public static void main(String[] args) {
Box b1 = new Box(20);
Box b2 = new Box(30);
System.out.println(b1.getObj() + b2.getObj());
}
\end{lstlisting}
Поскольку в коробке, с точки зрения языка, хранятся объекты, а оператор сложения для объектов не определён, следует применить операцию приведения типов. Средства языка не запрещают создать больше коробок, например, со строками, и будет производиться уже не складывание чисел, а конкатенация строк.
Проблема такого способа в том, что при каждом получении данных из коробки необходимо делать приведение типов, чтобы указать какие именно в контейнере лежат данные. Данную проблему для программы возможно решить организационно, если, например, называть переменные в венгерской нотации\footnote{Венгерская нотация в программировании -- соглашение об именовании переменных, констант и прочих идентификаторов в коде программ. Тип переменной указывается строчной буквой перед именем переменной, например для типа \code{int} переменная может называться \code{iMyVariable}.}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Согласование типов}]
public static void main(String[] args) {
Box b1 = new Box(20);
Box b2 = new Box(30);
System.out.println((Integer) b1.getObj() + (Integer) b2.getObj());
Box b3 = new Box("Hello, ");
Box b4 = new Box("World!");
System.out.println((String) b3.getObj() + (String) b4.getObj());
}
\end{lstlisting}
Вторая проблема в том, что данные в контейнере ничем не защищены. Java не запрещает менять данные внутри контейнера, поскольку для платформы это объект. Проблему возможно решить сделав проверку \code{instanceof} перед приведением типов.
Третья проблема в том, что все проблемы подобного рода проявляют себя только во время исполнения приложения, то есть у конечного пользователя перед глазами, когда разработчик ничего исправить не может.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Изменение типа хранения во время исполнения}]
public static void main(String[] args) {
Box iBox1 = new Box(20);
Box iBox2 = new Box(30);
if (iBox1.getObj() instanceof Integer && iBox2.getObj() instanceof Integer) {
int sum = (Integer) iBox1.getObj() + (Integer) iBox2.getObj();
System.out.println("sum = " + sum);
} else {
System.out.println("The contents of the boxes differ by type");
}
iBox1.setObj("sdf"); // Java: "What can go wrong here? You can do it!"
}
\end{lstlisting}
Таким образом, в языке Java возможно создавать классы, которые могут работать с любыми типами данных, но при любом обращении к таким классам и данным необходимо делать достаточно сложные проверки.
\subsubsection{Создание обобщения}
\begin{itemize}
\item Java generics -- это механизм языка, который позволяет создавать обобщенные (шаблонизированные) типы и методы в Java (особый подход к описанию данных и алгоритмов, позволяющий работать с различными типами данных без изменения внешнего описания);
\item Java generics были добавлены в Java 1.5 и стали одной из наиболее важных новых функций в языке;
\item Java generics работают только со ссылочными типами данных;
\item Java generics предоставляют безопасность типов во время компиляции, что означает, что ошибки связанные с типами данных могут быть обнаружены на этапе компиляции, а не во время выполнения программы.
\end{itemize}
Для описания обобщения в треугольных скобках пишется буква \code{<T>}, чтобы обозначить Type, Тип. На этапе описания класса невозможно сказать, какого типа данные будут лежать в переменной во время исполнения (число, строка или кот).
\begin{frm} \excl
Если написать \code{T} не в треугольных скобках при описании класса, то Java будет искать реально существующий класс, который она не видит.
\end{frm}
Таким образом указывается, что это обобщение и тип будет задаваться при создании объекта. Естественно поменять его будет нельзя, потому что Java -- это язык сильной статической типизации.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Создание обобщённого контейнера}]
private static class GBox<T> {
private T value;
public GBox(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public void showType() {
System.out.printf("Type is %s, with value %s\\n",
value.getClass().getName(), getValue());
}
}
public static void main(String[] args) {
GBox<String> stringBox = new GBox<>("Hello!");
stringBox.showType();
GBox<Integer> integerBox = new GBox<>(12);
integerBox.showType();
}
\end{lstlisting}
При вызове конструктора такого объекта, будет указано, что конструктор ожидает указанный ранее тип, а не \code{T}. При указании типа в левой части, этот тип подставляется во все места класса, где компилятор обнаружит \code{T}. При получении значений из \code{integerBox} и \code{stringBox} не требуется преобразование типов, \code{integerBox.getValue()} сразу возвращает \code{Integer}, а \code{stringBox.getValue()} -- \code{String}.
Если объект создан как \code{Integer}, то становится невозможно записать в него строку. При попытке написать такую строку кода, получится ошибка на этапе компиляции, то есть обобщения отслеживают корректность используемых типов данных.
По соглашению, переменные типа именуются одной буквой в верхнем регистре. Если обобщённых переменных более одной -- они пишутся через запятую.
\begin{frm} \info
\begin{itemize}
\item [] \code{E} -- элемент (Element, Entity обширно используется Java Collections);
\item [] \code{K} -- Ключ;
\item [] \code{N} -- Число;
\item [] \code{T} -- Тип;
\item [] \code{V} -- Значение;
\item [] \code{S}, \code{U}, и т. п. — 2-й, 3-й, 4-й типы.
\end{itemize}
\end{frm}
\subsubsection{Ограничения обобщений}
Обобщения накладывают на работу с собой некоторые ограничения.
\begin{enumerate}
\item Невозможно внутри метода обобщённого класса создать экземпляр параметризующего класса \code{T}, потому что на этапе компиляции об этом классе ничего не известно. Это ограничение возможно обойти, используя паттерн проектирования \textit{абстрактная фабрика}.
\item Нельзя создавать внутри обобщения массив из обобщённого типа данных. Но всегда можно подать такой массив снаружи.
\item По причине отсутствия информации о параметризируещем классе, невозможно создать статическое поле типа. Конкретный тип для параметра \code{T} становится известен только при создании \textbf{объекта} обобщённого класса.
\item Нельзя создать исключение обобщённого типа.
\end{enumerate}
\subsubsection{Работа с обобщёнными объектами}
При обращении к обобщённому классу необходимо заменить параметры типа на конкретные классы или интерфейсы, например строку, целое число или кота.
\begin{frm} \excl
«Параметр типа» и «аргумент типа» -- это два разных понятия. Когда объявляется обобщённый тип \code{GBox<T>}, то \code{T} является параметром типа, а когда происходит обращение к обобщённому типу, передается аргумент типа, например \code{Integer}.
\end{frm}
Как и любое другое объявление переменной запись вида \code{GBox<Integer> integerBox} сам по себе не создаёт экземпляр класса \code{GBox}. Такой код объявляет идентифиактор типа \code{GBox}, но сразу уточняет, что это будет коробка с целыми числами. Такой идентификатор обычно называется \textbf{параметризованным типом}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Способы создания обобщённых идентификаторов и объектов}]
GBox<Integer> integerBox0;
GBox<Integer> integerBox1 = new GBox<Integer>(1);
GBox<Integer> integerBox2 = new GBox<>(1);
\end{lstlisting}
Чтобы создать экземпляр класса, используется ключевое слово \code{new} и, в дополнение, указывается, что создаётся не просто \code{GBox}, а обобщённый, поэтому пишется \code{<Integer>}. Компиляторы, начиная с Java 1.7, научились самостоятельно подставлять в треугольные скобки нужный тип (\textbf{выведение типа из контекста}).
Если тип совпадает с аргументом типа в идентификаторе, в скобках экземпляра его можно не писать. Это называется \textbf{бриллиантовый оператор}.
\subsection{Варианты обобщений}
\subsubsection{Множество параметризированных типов}
Ограничений на количество параметризированных типов не накладывается. Часто можно встретить обобщения с двумя типами, например, в коллекциях, хранящих пары ключ-значение. Также, нет ограничений на использование типов внутри угловых скобок.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Множество параметризированных типов}]
private static class KVBox<K, V> {
private K key;
private V value;
public KVBox(K key, V value) {
this.key = key;
this.value = value;
}
public V getValue() {
return value;
}
public K getKey() {
return key;
}
public void showType() {
System.out.printf("Type of key is %s, key = %s, " +
"type of value is %s, value = %s\\n",
key.getClass().getName(), getKey(),
value.getClass().getName(), getValue());
}
}
public static void main(String[] args) {
KVBox<Integer, String> kvb0 = new KVBox<>(1, "Hello");
KVBox<String, GBox<String>> kvb1 = new KVBox<>("World", new GBox<>("Java"));
}
\end{lstlisting}
\subsubsection{Raw type (сырой тип)}
Сырой тип -- это имя обобщённого класса или интерфейса без аргументов типа, то есть это, фактически, написание идентификатора и вызов конструктора обобщённого класса как обычного, без треугольных скобок. При использовании сырых типов, программируется поведение, которое существовало до введения обобщений в Java.
Геттеры сырых типов возвращают объекты. Это логично, потому что ни на одном из этапов не указан аргумент типа.
\begin{frm} \excl
\code{GBox} -- это сырой тип обобщённого типа \code{GBox<T>}. Однако необобщённый класс или интерфейс не являются сырыми типами.
\end{frm}
Для совместимости со старым кодом допустимо присваивать параметризованный тип своему собственному сырому типу.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Использование сырых типов}]
GBox<Integer> intBox = new GBox<>(1);
GBox box = intBox;
GBox box = new GBox(1);
GBox<Integer> intBox = box;
GBox<Integer> intBox0 = new GBox<>(1);
GBox box0 = intBox0;
box.setValue(4);
\end{lstlisting}
Также, если присвоить параметризованному типу сырой тип, или если попытаться вызвать обобщённый метод в сыром типе, то буквально каждое слово в программе будет с предупреждением среды разработки. Предупреждения показывают, что сырой тип обходит проверку обобщённого типа, что откладывает обнаружение потенциальной ошибки на время выполнения программы.
Предупреждения среды, а на самом деле, предупреждения компилятора, обычно имеют вид, представленный на рис \hrf{pic:ide-unchecked}.
\begin{figure}[H]
\centering
\includegraphics[width=15cm]{jd-03-ide-unchecked.jpeg}
\caption{Предупреждение среды о непроверенном типе}
\label{pic:ide-unchecked}
\end{figure}
Термин «unchecked» означает непроверенные, то есть компилятор не имеет достаточного количества информации для обеспечения безопасности типов. По умолчанию этот вид предупреждений выключен, поэтому компилятор в терминале на самом деле даёт подсказку.
\begin{verbatim}
Note: <ClassName>.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
\end{verbatim}
Чтобы увидеть все «unchecked» предупреждения нужно перекомпилировать код с опцией \code{-Xlint:unchecked}.
\begin{figure}[H]
\centering
\includegraphics[width=15cm]{jd-03-lint-unchecked.jpeg}
\caption{Предупреждение компилятора о непроверенном типе}
% \label{pic:}
\end{figure}
К предупреждениям среды в написанном коде желательно относиться внимательно, и придирчиво перепроверять код на наличие ненадёжных конструкций.
\subsubsection{Вопросы для самопроверки}
\begin{enumerate}
\item Обобщения это способ создания общих
\begin{enumerate}
\item классов для разных пакетов;
\item алгоритмов для разных типов данных;
\item библиотек для разных приложений.
\end{enumerate}
\item Что именно значит буква в угловых скобках?
\begin{enumerate}
\item Название обобщённого класса;
\item имя класса, с которым будет работать обобщение;
\item название параметра, используемого в обобщении.
\end{enumerate}
\item Возможно ли передать обобщённым аргументом примитивный тип?
\begin{enumerate}
\item Да;
\item нет;
\item только строку.
\end{enumerate}
\end{enumerate}
\subsubsection{Обобщённые методы}
Обобщённые методы синтаксически схожи с обобщёнными классами, но параметры типа относятся к методу, а не к классу. Допустимо делать обобщёнными статические и не статические методы, а также конструкторы.
Синтаксис обобщённого метода включает параметры типа внутри угловых скобок, которые указываются перед возвращаемым типом.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обобщённый метод}]
private static <T> void setIfNull(GBox<T> box, T t) {
if (box.getValue() == null) {
box.setValue(t);
}
}
public static void main(String[] args) {
GBox<Integer> box = new GBox<>(null);
setIfNull(box, 13);
System.out.println(box.getValue());
GBox<Integer> box0 = new GBox<>(1);
setIfNull(box0, 13);
System.out.println(box0.getValue());
\end{lstlisting}
\subsection{Ограниченные параметры типа}
Bounded type parameters позволяют ограничить типы данных, которые могут быть использованы в качестве параметров.
Использование bounded type parameters в Java является хорошей практикой, которая позволяет более точно определить используемые типы данных и обеспечивает более безопасный и читаемый код.
\subsubsection{Ограничение «сверху»}
В качестве примера необходимости ограничений будет использоваться уже написанная коробка. В коробке описывается операция сложения. Если сложение чисел и конкатенация строк -- это понятные операции, то сложение котиков или потоков ввода-вывода не определены. Применение \textbf{bounded type parameters} позволяет более точно задавать ограничения на используемые типы данных, что помогает писать более безопасный и читаемый код. То есть, в обобщениях существует возможность ограничиться только теми типами, которые соответствуют определенным требованиям
Например, коробку предлагается сделать так, чтобы в ней хранились только числа -- наследники класса \code{Number}. Подобное ограничение делается с помощью ограниченного параметра типа (bounded type parameter). Чтобы объявить «ограниченный сверху» параметр типа необходимо после имени параметра указать ключевое слово \code{extends}, а затем указать верхнюю границу (upper bound), которой в данном примере является класс \code{Number}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Ограничение параметра типа «сверху»}]
public class Box<V extends Number> {
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
private V value;
}
private static <T extends Number> void setIfNull(BBox<T> box, T t) {
if (box.getValue() == null) {
box.setValue(t);
}
}
public static void main(String[] args) {
BBox<Integer> integerBBox = new BBox<>();
BBox<String> stringBBox = new BBox<>();
setIfNull(integerBBox, 4);
setIfNull(stringBBox, "hello");
}
\end{lstlisting}
В рассматриваемом примере типы, которые можно использовать в параметризованных классах \code{Box}, ограничены наследниками класса \code{Number}. Если попытаться создать переменную с типом, например, \code{Box<String>}, то возникнет ошибка компиляции. Аналогичным образом создаются обобщённые методы с ограничением.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Множественные ограничения}]
class Bird{}
interface Animal{}
interface Man{}
class CBox<T extends Bird & Animal & Man> {
// ...
}
\end{lstlisting}
Также возможно задать несколько границ. При этом, важно помнить, что также, как и в объявлении классов, возможен только один класс-наследник и он указывается первым. Все остальные границы могут быть только интерфейсами и указываются через знак амерсанда.
\begin{frm} \info
В языке Java, несмотря на строгость типизации, возможно присвоить идентификатору одного типа объект другого типа, если эти типы \textbf{совместимы}. Например, можно присвоить объект типа \code{Integer} переменной типа \code{Object}, так как \code{Object} является одним из супертипов \code{Integer}. В объектно-ориентированной терминологии это называется связью «является» («is a»).
\end{frm}
Предположим, что существует метод, описанный с generic параметром.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Generic параметр метода}]
private static void boxTest(GBox<Number> n) { /* ... */ }
public static void main(String[] args) {
boxTest(new GBox<Number>(10));
boxTest(new GBox<Integer>(1)); // compile error
boxTest(new GBox<Float>(1.0f)); // compile error
\end{lstlisting}
На первый взгляд кажется, что в метод возможно передать \code{Box<Integer>} или \code{Box<Double>}, но нельзя, так как \code{Box<Integer>} и \code{Box<Double>} не являются потомками Box<Number>.
\begin{frm} \excl
Это частое недопонимание принципов работы обобщений, и это важно знать. Наследование не работает в Java generics так, как оно работает в обычной Java.
\end{frm}
Идентификатор коробки с \code{Number} не может в себе хранить коробку с \code{Integer}. Обобщение защищает от попыток положить в коробку, например, строк -- не строку. То есть, предположим, в коробку кладётся \code{Integer}, как наследник \code{Number}, а затем, например \code{Float}, получится путаница.
Из приведённого примера возможно сделать вывод о том, что если методу с таким параметром передать коробку с \code{Integer} -- он не будет работать. Чтобы допустить передачу таких контейнеров, в аргументе следует указать что в параметре возможен любой тип, являющийся наследником \code{Number}, то есть использовать маску \code{<? extends Number>}. Ограничивать такую маску возможно как сверху так и снизу. Таким образом, обобщения защищают самих себя от путаницы и не дают складывать в одни и те же контейнеры разные типы данных.
Поскольку коробку с чем то ещё, кроме \code{Number} и его наследников создавать нельзя, маск\'{и}рование при вызове метода будет избыточно
На самом деле обобщения -- это так называемый синтаксический сахар. То есть, когда в коде используется обобщение, во время компиляции произойдёт так называемое «стирание», и все обобщённые типы данных преобразуются в \code{Object}, соответствующие проверки и приведения типов.
\subsubsection{Ограничение «снизу»}
Ограничение типов возможно вводить как сверху, так и снизу. На примере обобщённых методов. Такие методы необходимы, когда требуется объединить несколько похожих, но всё же разных типов данных. Если подать на вход обобщённого метода два типа -- \code{Integer} и \code{Float} в итоге для работы будет выбран ближайший старший для них обоих -- \code{Number}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Пример обобщённого метода}]
private static <T extends Number> boolean compare(T src, T dst) {
return src.equals(dst);
}
public static void main(String[] args) {
System.out.println(compare(1, 1.0f));
System.out.println(compare(1.0f, 1.0f));
System.out.println(compare(1, 1));
\end{lstlisting}
Например, даны два списка -- один с \code{Integer} другой с \code{Number}, и требуется написать метод, который будет перекидывать числа из одного списка в другой. Для совершения этого действия описан метод, при работе которого возможны два сценария -- копировать элементы \code{Number} в список \code{Integer} или элементы \code{Integer} в список \code{Number}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Копирование чисел в списки}]
public static void copyTo(ArrayList src, ArrayList dst) {
for (Object o : src) dst.add(o);
}
public static void main(String[] args) {
ArrayList<Integer> ial = new ArrayList<>(Arrays.asList(1, 2, 3));
ArrayList<Number> nal = new ArrayList<>(Arrays.asList(1f, 2, 3.0));
System.out.println(ial);
System.out.println(nal);
copyTo(ial, nal);
System.out.println(nal);
copyTo(nal, ial);
System.out.println(ial);
\end{lstlisting}
Правильным рабочим сценарием будет только второй -- копирование более точного типа в более общий список. Java при компиляции пропустит в работу оба сценария, но действительно работающим всё равно остаётся только один.
Для примера, описаны два класса: «животное» и наследник животного -- «кот». На их основе создаются обобщённые списки и вызывается обобщённый метод копирования списков.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Копирование списков с «животными» и «котами»}]
public static void copyTo(ArrayList src, ArrayList dst) {
for (Object o : src) dst.add(o);
}
private static class Animal {
protected String name;
protected Animal() { this.name = "Animal"; }
@Override public String toString() { return name; }
}
private static class Cat extends Animal {
protected Cat() { this.name = "Cat"; }
}
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>(Arrays.asList(new Cat()));
ArrayList<Animal> animals = new ArrayList<>(Arrays.asList(new Animal()));
copyTo(animals, cats);
System.out.println(cats);
\end{lstlisting}
При компиляции и исполнении ошибок не возникло. К классу кота добавляется метод голос и совершается попытка вызвать голос у объекта из списка.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-lists-merged-problem.jpeg}
\caption{Проблема объединения списков}
\end{figure}
При попытке вызвать у более общего «животного» метод более частного «кота» выбрасывается исключение о невозможности приведения типов.
Для того, чтобы избежать подобных проблем, необходимо описать метод таким образом, чтобы он работал только с каким-то одним типом \code{<T>} и списки будут \code{<T>} и в цикле тоже будут перебираться элементы типа \code{T}. Если более не уточнять, получится, что в список котов возможно класть только котов, а в список животных класть только животных. Но кот -- это наследник животного, его присутствие в списке животных уместно. Получается, что источником может быть список из заданного типа или его наследников, а приёмником -- тип или его родители, и далее метод, виртуальная машина и другие механизмы сами разбираются, кто подходит под эти параметры.
При таком описании метода, неверные варианты будут отсекаться на этапе компиляции.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={}]
public static <T> void copyTo (
ArrayList<? extends T> src, ArrayList<? super T> dst) {
for (T o : src) {
dst.add(o);
}
}
\end{lstlisting}
\subsection{Выведение типов}
Алгоритм выведения типов определяет типы аргументов, а также, если это применимо, тип, в который присваивается результат или в котором возвращается результат.
\begin{frm} \info
Выведение типов -- это возможность компилятора автоматически определять аргументы типа на основе контекста.
\end{frm}
Алгоритм работает от наиболее общего типа (\code{Object}) к наиболее точному, подходящему к данной ситуации. Например, добавив к коту реализацию интерфейса \code{Serializable} и написав метод, работающий с одним и только одним типом данных \code{T} будет создана ситуация, в которой никакие аргументы не связаны наследованием.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Ситуация, в которой сработает выведение типов}]
private static class Cat extends Animal implements Serializable {
protected Cat() { this.name = "Cat"; }
public void voice(){ System.out.println("meow"); }
}
private static <T> T pick(T first, T second) { return second; }
public static void main(String[] args) {
Serializable se1 = pick("d", new Cat());
Serializable se2 = pick("d", new ArrayList<String>());
\end{lstlisting}
В таком методе выведение типов определяет, что вторые аргументы метода \code{pick}, а именно \code{Cat} и \code{ArrayList}, передаваемые в метод имеют тип \code{Serializable}, но этого недостаточно, потому что первый аргумент тоже должен быть того же типа. Удачно, что строка -- это тоже \code{Serializable}.
В описании обобщённых методов, выведение типа делает возможным вызов обобщённого метода так, будто это обычный метод, без указания типа в угловых скобках.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Выведение типов в обобщённых методах},label={lst:generic-type-resolve}]
public class App {
public static <U> void addBox(U u, List<Box<U>> boxes) {
Box<U> box = new Box<>();
box.setValue(u);
boxes.add(box);
}
public static void main( String[] args ) {
ArrayList<Box<Cat>> catsInBoxes = new ArrayList<>();
App.<Cat>addBox(new Cat("Kusya"), catsInBoxes);<@\label{lst-line:with-type}@>
addBox(new Cat("Kusya"), catsInBoxes);<@\label{lst-line:no-type}@>
addBox(new Cat("Murka"), catsInBoxes);
printBoxes(catsInBoxes);
}
\end{lstlisting}
Очевидно, что в листинге \hrf{lst:generic-type-resolve} обобщённый метод \code{addBox()} объявляет один параметр типа \code{U}. В большинстве случаев компилятор Java может вывести параметры типа вызова обобщённого метода, в результате чаще всего вовсе не обязательно их указывать.
Чтобы вызвать обобщённый метод \code{addBox()}, возможно указать параметры типа (строка \hrf{lst-line:with-type}) либо опустить их, тогда компилятор языка автоматически выведет тип Cat из аргументов метода при вызове (строка \hrf{lst-line:no-type}).
Выведение типа при создании экземпляра обобщённого класса позволяет заменить аргументы типа, необходимые для вызова конструктора обобщённого класса пустым множеством параметров типа (пустые треугольные скобки, бриллиантовая операция), так как компилятор может вывести аргументы типа из контекста.
Очевидно, что конструкторы могут быть обобщёнными как в обобщённых, так и в необобщённых классах.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обобщённый конструктор}]
public class Box<T> {
<U> Box(U u){
// ...
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
private T value;
}
public static void main(String[] args) {
Box<Cat> box = new Box<Cat>("Some message");
\end{lstlisting}
Например, возможно явно указать, что у коробки будет обобщённый аргумент «кот», а у конструктора -- какой-то другой аргумент, например, строка или число. Компилятор выведет тип String для формального параметра U, так как фактически переданный аргумент является экземпляром класса String.
\begin{frm} \excl
Алгоритм выведения типа использует только аргументы вызова, целевые типы и возможно очевидный ожидаемый возвращаемый тип для выведения типов. Алгоритм выведения не использует последующий код программы.
\end{frm}
\subsection{Целевые типы}
Компилятор Java пользуется целевыми типами для вывода параметров типа вызова обобщённого метода.
\begin{frm} \info
Целевой тип выражения -- это тип данных, который компилятор Java ожидает в зависимости от того, в каком месте находится выражение.
\end{frm}
Например, как в методе \code{emptyBox()} (листинг \hrf{lst:target-types}, строка \hrf{lst-line:empty-box}). В основном методе инициализация ожидает экземпляр \code{Box<String>}. Этот тип данных является целевым типом. Поскольку метод \code{emptyBox()} возвращает значение обобщённого типа \code{Box<T>}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Целевые типы},label={lst:target-types}]
public class TBox<T> {
public static final TBox EMPTY_BOX = new TBox<>();
public T getValue() { return value; }
public void setValue(T value) {
this.value = value;
}
private T value;
static <T> TBox<T> emptyBox(){<@\label{lst-line:empty-box}@>
return (TBox<T>) EMPTY_BOX;
}
}
public static void main( String[] args ) {
TBox<String> box = TBox.emptyBox();
}
\end{lstlisting}
\subsection{Вопросы для самопроверки}
\begin{enumerate}
\item Что из следующего является недопустимым?
\begin{enumerate}
\item \code{ArrayList<? extends Number> al1 = new ArrayList<Number>();}
\item \code{ArrayList<? extends Number> al2 = new ArrayList<Integer>();}
\item \code{ArrayList<? extends Number> al3 = new ArrayList<String>();}
\item Всё допустимо.
\end{enumerate}
\item параметры метода \code{ArrayList<? extends T> src, ArrayList<? super T> dst} вызов метода \code{copyTo(cats, animals);} Какой тип данных будет взят в качестве \code{Т}?
\begin{enumerate}
\item Animal;
\item Cat;
\item Object.
\end{enumerate}
\end{enumerate}
\subsection{Подстановочный символ \code{<?>} (wildcard)}
В обобщённом коде знак вопроса, называемый подстановочным символом, означает неизвестный тип. Подстановочный символ может использоваться в разных ситуациях: как параметр типа, поля, локальной переменной, иногда в качестве возвращаемого типа. Подстановочный символ никогда не используется (рис \hrf{pic:wildcard-errors}) в качестве аргумента типа для вызова обобщённого метода, создания экземпляра обобщённого класса или супертипа.
\begin{figure}[H]
\centering
\includegraphics[width=16cm]{jd-03-wildcard-error.jpeg}
\caption{Ошибки при работе с подстановочным символом}
\label{pic:wildcard-errors}
\end{figure}
Передаваемые типы для уточнения, возможно \textbf{ограничивать} сверху и снизу. Ограниченный сверху подстановочный символ применяется, чтобы \textbf{ослабить ограничения} переменной.
\begin{figure}[H]
\centering
\fontsize{12}{1}\selectfont
\includesvg[scale=1.01]{pics/jd-03-wildext.svg}
\end{figure}
Чтобы написать метод, который работает с коробками, в которых содержится \code{Number} и дочерние от \code{Number} типы, например \code{Integer}, \code{Double} и другие, необходимо указать \code{Box<? extends Number>}.
\begin{figure}[H]
\centering
\fontsize{12}{1}\selectfont
\includesvg[scale=1.01]{pics/jd-03-nowild.svg}
\end{figure}
Пример кода будет приводиться с классами животного и наследников.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Подготовка классов для демнстрации},label={}]
private static class Animal {
protected String name;
protected Animal(String name) { this.name = name; }
@Override public String toString() {
return this.getClass().getSimpleName() + " with name " + name;
}
}
private static class Cat extends Animal {
protected Cat(String name) { super(name); }
}
private static class Dog extends Animal {
protected Dog(String name) { super(name); }
}
\end{lstlisting}
Внутри метода, выводящего информацию о содержимом коробки присутствует ограниченный сверху подстановочный символ \code{<? extends Animal>}, где вместо \code{Animal} может быть любой тип.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Использование ограниченного подстановочного символа},label={lst:wildcard-extended}]
public static class TBox<T> {
public static final TBox EMPTY_BOX = new TBox<>();
private T value;
public T getValue() { return value; }
public void setValue(T value) { this.value = value; }
static <T> TBox<T> emptyBox() {
return (TBox<T>) EMPTY_BOX;
}
@Override public String toString() {
return value.toString();
}
}
static void printInfo(TBox<? extends Animal> animalInBox) {<@\label{lst-line:printinfo}@>
System.out.println("Information about animal: " + animalInBox);
}
\end{lstlisting}
Создав соответствующие объекты, положив их в коробки и запустив код возможно наблюдать, что коробка вмещает как животных, так и наследников животного, чего не произошло бы при использовании в параметре типа животного без подстановочного символа.
\begin{frm} \info
Если просто использовать подстановочный символ \code{<?>}, то получится подстановочный символ без ограничений. \code{Box<?>} означает коробку с неизвестным содержимым (неизвестным типом).
\end{frm}
Такой синтаксис существует для того, чтобы продолжать использовать обобщённый тип без уточнения типа, как следствие, без использования обобщённой функциональности. Неограниченный подстановочный символ полезен, если нужен метод, который может быть реализован с помощью функциональности класса \code{Object}. Когда код использует методы обобщённого класса, которые не зависят от параметра типа.
\begin{frm} \info
В программах, использующих Reflection API конструкция \code{Class<?>} используется чаще других конструкций, потому что большинство методов объекта Класс не зависят от расположенного внутри типа.
\end{frm}
Например, метод \code{printInfo()} (строка \hrf{lst-line:printinfo}) из листинга \hrf{lst:wildcard-extended}, не использует никаких методов животного, цель метода -- вывод в консоль информации об объекте в коробке любого типа, поэтому в параметре метода можно заменить «коробку с наследниками животного» на «коробку с чем угодно», ведь в методе будет использоваться только метод коробки \code{toString()}.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Исправление метода вывода информации на экран},label={}]
static void printInfo(TBox<?> animalInBox) {
System.out.println("Information about animal: " + animalInBox);
}
\end{lstlisting}
\begin{frm} \excl
Важно запомнить, что \code{Box<Object>} и \code{Box<?>} -- это не одно и то же.
\end{frm}
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-wildcard-compile-error.jpeg}
\caption{Ошибка компиляции при использовании параметра типа \code{Object}}
\end{figure}
Ограниченный снизу подстановочный символ ограничивает неизвестный тип так, чтобы он был либо указанным типом, либо одним из его предков.
В обобщённых конструкциях возможно указать либо только верхнюю границу для подстановочного символа, либо только нижнюю.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-no-both-borders.jpeg}
\end{figure}
То есть, если написать метод \code{printInfo()}, с параметром коробки и обобщённым аргументом не \code{<? extends Animal>}, а \code{<? super Animal>}, токод также не будет работать, потому что метод будет ожидать не «животное и наследников», а «животное и родителей», то есть \code{Object}.
\begin{figure}[H]
\centering
\fontsize{12}{1}\selectfont
\includesvg[scale=1.01]{pics/jd-03-wildsup.svg}
\end{figure}
Обобщённые классы или интерфейсы связаны не только из-за связи между их типами. С обычными, необобщёнными классами, наследование работает по правилу подчинённых типов: класс \code{Cat} является подклассом класса \code{Animal}, и расширяет его.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Наследование необобщённых классов},label={}]
private static class Animal {
protected String name;
protected Animal(String name) { this.name = name; }
@Override public String toString() {
return this.getClass().getSimpleName() + " with name " + name;
}
}
private static class Cat extends Animal {
protected Cat(String name) { super(name); }
}
public static void main(String[] args) {
Cat cat = new Cat("Vasya");
Animal animal = cat;
TBox<Cat> catInBox = new TBox<>();
TBox<Animal> animalInBox = catInBox; // Incompatible types
\end{lstlisting}
Данное правило не работает для обобщённых типов. Несмотря на то, что \code{Cat} является подтипом \code{Animal}, \code{Box<Cat>} не является подтипом \code{Box<Animal>}. Общим предком для \code{Box<Animal>} и \code{Box<Cat>} является \code{Box<?>}.
\begin{figure}[H]
\centering
\fontsize{12}{1}\selectfont
\includesvg[scale=1.01]{pics/jd-03-wildparent.svg}
\end{figure}
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Наследование в параметрах типа через подстановочнвй символ},label={}]
TBox<? extends Cat> catInBox = new TBox<>();
TBox<? extends Animal> animalInBox = catInBox; // OK
\end{lstlisting}
В некоторых случаях компилятор может вывести тип подстановочного символа. Коробка может быть определена как \code{Box<?>}, но при вычислении выражения компилятор выведет конкретный тип из кода, такой сценарий называется \textbf{захватом подстановочного символа}. В большинстве случаев нет нужды беспокоиться о захвате подстановочного символа, кроме случаев, когда в сообщении об ошибке появляется фраза \code{capture of}. В примере, приведённом в листинге \hrf{lst:capture-error} компилятор обрабатывает параметр коробки как тип \code{Object}. Когда вызывается метод внутри \code{testError()}, компилятор не может подтвердить тип объекта, который будет присутствовать внутри коробки и генерирует ошибку. Обобщения были добавлены в Java именно для этого -- чтобы усилить безопасность типов на этапе компиляции.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Ошибка захвата подстановочного символа},label={lst:capture-error}]
static void testError(Box<?> box){
box.setValue(box.getValue()); // capture of ?
}
\end{lstlisting}
Когда есть уверенность в том, что операция безопасна, ошибку возможно исправить написав приватный вспомогательный метод (в англоязычной литературе private helper method), захватывающий подстановочный символ.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Использование вспомогательного метода},label={}]
private static <T> void testErrorHelper(TBox<T> box) {
box.setValue(box.getValue());
}
static void testError(TBox<?> box) {
testErrorHelper(box);
}
\end{lstlisting}
\subsubsection{Краткое руководство по использованию подстановочных символов}
\textbf{Входная переменная.} Предоставляет данные для кода. Для метода \code{copy(src, dst)} параметр \code{src} предоставляет данные для копирования, поэтому он считается входной переменной.
\textbf{Выходная переменная.} Содержит данные для использования в другом месте. В примере \code{copy(src, dst)} параметр \code{dst} принимает данные и будет считаться выходной переменной.
\begin{enumerate}
\item Входная переменная определяется с ограничением сверху.
\item Выходная переменная определяется с ограничением снизу.
\item Если ко входной переменной можно обращаться только как к \code{Object} -- неограниченный подстановочный символ.
\item Если переменная должна использоваться как входная и как выходная одновременно, НЕ использовать подстановочный символ.
\item Не использовать подстановочные символы в возвращаемых типах.
\end{enumerate}
\subsubsection{Вопросы для самопроверки}
\begin{enumerate}
\item Ограниченный снизу подстановочный символ позволяет передать в качестве аргумента типа
\begin{enumerate}
\item только родителей типа;
\item только наследников типа;
\item сам тип и его родителей;
\item сам тип и его наследников.
\end{enumerate}
\item Конструкция <? super Object>
\begin{enumerate}
\item не скомпилируется;
\item не имеет смысла;
\item не позволит ничего передать в аргумент типа;
\item не является чем-то необычным.
\end{enumerate}
\end{enumerate}
\subsection{Стирание типа и загрязнение кучи}
Обобщения были введены в язык программирования Java для обеспечения более жёсткого контроля типов во время компиляции и для поддержки обобщённого программирования. Для реализации обобщения компилятор Java применяет стирание типа.
\subsubsection{Стирание типа}
\begin{itemize}
\item Механизм стирания типа фактически заменяет все параметры типа в обобщённых типах их границами или \code{Object}, если параметры типа не ограничены.
\item Сгенерированный байткод содержит только обычные классы, интерфейсы и методы.
\item Вставляет явное приведение типов где необходимо.
\item Генерирует связующие методы, чтобы сохранить полиморфизм в расширенных обобщённых типах.
\item Гарантирует, что никакие новые классы не будут созданы для параметризованных типов, следовательно обобщения не приводят к накладным расходам во время исполнения.
\end{itemize}
Компилятор Java также стирает параметры типа обобщённых методов. Обобщённый метод в котором используется неограниченный тип будет заменён компилятором на \code{Object}.
Аналогично классам для методов происходит стирание типа при расширении ключевым словом \code{extends} -- параметр в угловых скобках заменяется на максимально возможного родителя.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={},label={}]
private static <T> void setIfNull(TBox<T> box, T t) {
if (box.getValue() == null) {
box.setValue(t);
}
}
// ... both methods have same erasure
private static void setIfNull(TBox<Object> box, Object t) {
if (box.getValue() == null) {
box.setValue(t);
}
}
\end{lstlisting}
Стирание типа имеет последствия, связанные с произвольным количеством параметров (varargs).
\textbf{Материализуемые типы} -- это типы, информация о которых полностью доступна во время выполнения, такие как примитивы, необобщённые типы, сырые типы, обращения к неограниченным подстановочным символам.
\textbf{Нематериализуемые типы} -- это типы, информация о которых удаляется во время компиляции стиранием типов, например, обращения к обобщённым типам, которые не объявлены с помощью неограниченных подстановочных символов. Во время выполнения о нематериализуемых типах нет всей информации. Виртуальная машина Java не может узнать разницу между нематериализуемыми типами во время выполнения.
\subsubsection{Загрязнение кучи}
Загрязнение кучи (heap pollution) возникает, когда переменная параметризованного типа ссылается на объект, который не является параметризованным типом. Такая ситуация возникает, если программа выполнила операцию, генерирующую предупреждение \code{unchecked warning} во время компиляции. Предупреждение \code{unchecked warning} генерируется, если правильность операции, в которую вовлечён параметризованный тип (например, приведение типа или вызов метода) не может быть проверена.
Если компилируются различные части кода отдельно, то становится трудно определить потенциальную угрозу загрязнения кучи.
\subsection{Ограничения обобщений (резюме)}
В этом разделе приводится некоторое резюме вышесказанного в части наиболее частых ошибок при работе с обобщениями.
Нельзя использовать при создании экземпляра примитивы. Выход из ситуации -- использование классов-обёрток.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-01.jpeg}
\end{figure}
Нельзя создавать экземпляры параметров типа. В качестве выхода из ситуации -- передавать вновь созданный объект в обобщённые методы в качестве параметра.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-02.jpeg}
\end{figure}
Статические поля класса являются общими для всех объектов этого класса, поэтому статические поля с типом параметра типа запрещены. Так как статическое поле является общим для коробки с животным, коробки с котом и коробки с собакой, то какого типа это поле? Также запрещено использовать приведение типа к параметризованному типу, если он не использует неограниченный подстановочный символ.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-03.jpeg}
\end{figure}
Запрещено использовать приведения типов для обобщённых объектов. Так как компилятор стирает все параметры типа из обобщённого кода, то нельзя проверить во время выполнения, какой параметризованный тип используется для обобщённого типа.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-04.jpeg}
\end{figure}
Запрещено создавать массивы параметризованных типов.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-05.jpeg}
\end{figure}
Обобщённый класс не может расширять класс \code{Throwable} напрямую или опосредовано. Метод не может ловить (\code{catch}) экземпляр параметра типа. Однако можно использовать параметр типа в \code{throws}.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-06.jpeg}
\end{figure}
Класс не может иметь два перегруженных метода, которые будут иметь одинаковую сигнатуру после стирания типов. Такой код не скомпилируется.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jd-03-38-08.jpeg}
\end{figure}
\subsection*{Практическое задание}
\begin{enumerate}
\item Написать метод, который меняет два элемента массива местами (массив может быть любого ссылочного типа);
\item Большая задача:
\begin{itemize}
\item Есть классы \code{Fruit} -> \code{Apple}, \code{Orange}; (больше не надо);
\item Класс \code{Box} в который можно складывать фрукты, коробки условно сортируются по типу фрукта, поэтому в одну коробку нельзя сложить и яблоки, и апельсины; Для хранения фруктов внутри коробки можете использовать \code{ArrayList};
\item Сделать метод \code{getWeight()} который высчитывает вес коробки, зная количество фруктов и вес одного фрукта(вес яблока -- \code{1.0f}, апельсина -- \code{1.5f}, не важно в каких единицах);
\item Внутри класса коробки сделать метод \code{compare()}, который позволяет сравнить текущую коробку с той, которую подадут в \code{compare()} в качестве параметра, \code{true} -- если их веса равны, \code{false} в противном случае (коробки с яблоками возможно сравнивать с коробками с апельсинами);
\item Написать метод, который позволяет пересыпать фрукты из текущей коробки в другую коробку (при этом, нельзя яблоки высыпать в коробку с апельсинами), соответственно, в текущей коробке фруктов не остается, а в другую перекидываются объекты, которые были в этой коробке.
\end{itemize}
\end{enumerate}
\newpage
\printnomenclature[40mm]
\end{document}

624
jtd3-08-workshop.tex Normal file
View File

@ -0,0 +1,624 @@
\documentclass[j-spec.tex]{subfiles}
\usepackage{spreadtab}
\begin{document}
\section{Семинар: Обобщения}
\subsection{Инструментарий}
\begin{itemize}
\item \href{https://docs.google.com/presentation/d/1rWHGQBITRElvyKFEjwe8dOinDbAV2ZW18tAK1FXU48U/edit?usp=share_link}{Презентация} для преподавателя, ведущего семинар;
\item \href{https://drive.google.com/file/d/1LWyE8aEy4-1gsognqhXIXwDcoLviVge4/view}{Фон} GeekBrains для проведения семинара в Zoom;
\item JDK любая 11 версии и выше;
\item \href{https://www.jetbrains.com/idea/download}{IntelliJ IDEA Community Edition} для практики и примеров используется IDEA.
\end{itemize}
\subsection{Цели семинара}
\begin{itemize}
\item Практика создания простых обобщений;
\item Создание собственного обобщённого контейнера с описанием базовых алгоритмов (введение в коллекции);
\item Изучение поведения объекта итератора;
\item Закрепление понимания механизмов работы коллекций.
\end{itemize}
\subsection{План-содержание}
\noindent
\begin{spreadtab}{{longtable}{|p{37mm}|l|l|p{90mm}|}}
\hline
@ Что происходит & @ Время & @ Слайды & @ Описание \\
\hline
\endhead
@ Организационный момент & 5 tag(beg) & @ 1-5 & @ Преподаватель ожидает студентов, поддерживает активность и коммуникацию в чате, озвучивает цели и планы на семинар. Важно упомянуть, что выполнение домашних заданий с лекции является, фактически, подготовкой к семинару \\
\hline
@ Quiz & 5 & @ 6-18 & @ Преподаватель задаёт вопросы викторины, через 30 секунд демонстрирует слайд-подсказку и ожидает ответов (по минуте на ответ) \\
\hline
@ Рассмотрение ДЗ лекции & 10 & @ 19-23 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
\hline
@ Вопросы и ответы & 10 & @ 24 & @ Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы \\
\hline
@ Задание 1 & 10 & @ 25-27 & @ Формирование навыков базовой работы с обобщениями \\
\hline
@ Задание 2 & 20 & @ 28-30 & @ Практика написания обобщённого кода с дополнительным описанием алгоритмов манипулирования данными \\
\hline
@ Перерыв (если нужен) & 5 & @ 31 & @ Преподаватель предлагает студентам перерыв на 5 минут (студенты голосуют) \\
\hline
@ Задание 3 & 20 & @ 32-34 & @ Изучение поведения итератора для понимания механизмов его действия и необходимости его использования в работе \\
\hline
@ Задание 4 & 20 & @ 35-38 & @ Закрепление понимания механизмов работы коллекций (в том числе с применением итераторов) \\
\hline
@ Домашнее задание & 5 & @ 39-40 & @ Объясните домашнее задание, подведите итоги урока \\
\hline
@ Рефлексия & 10 tag(end) & @ 41-42 & @ Преподаватель запрашивает обратную связь \\
\hline
@ Длительность & sum(cell(beg):cell(end)) & & \\
\hline
\end{spreadtab}
\subsection{Подробности}
\subsubsection*{Организационный момент}
\begin{itemize}
\item \textbf{Цель этапа:} Позитивно начать урок, создать комфортную среду для обучения.
\item \textbf{Тайминг:} 3-5 минут.
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Запрашивает активность от аудитории в чате;
\item Презентует цели курса и семинара;
\item Презентует краткий план семинара и что студент научится делать.
\end{itemize}
\end{itemize}
\subsubsection*{Quiz}
\begin{itemize}
\item \textbf{Цель этапа:} Вовлечение аудитории в обратную связь.
\item \textbf{Тайминг:} 5--7 минут (4 вопроса, по минуте на ответ).
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель задаёт вопросы викторины, представленные на слайдах презентации;
\item через 30 секунд демонстрирует слайд-подсказку и ожидает ответов.
\end{itemize}
\item \textbf{Вопросы и ответы:}
\begin{enumerate}
\item Какая основная цель использования Java generics? (1)
\begin{enumerate}
\item Упрощение программирования и повышение безопасности типов
\item Ускорение работы программы и сокращение объема кода
\item Позволяют работать с различными базами данных одновременно
\end{enumerate}
% Неверные ответы: b) и c) - generics не связаны с ускорением работы программы или работой с базами данных, их основная цель - добавить типовую безопасность и упростить программирование.
\item Что такое параметризованный класс в Java generics? (1)
\begin{enumerate}
\item Класс, который принимает параметр типа
\item Класс, который имеет только один параметр типа
\item Класс, который можно использовать только с определенным типом данных
\end{enumerate}
% Неверные ответы: b) и c) - параметризованный класс может иметь любое количество параметров типа и может быть использован с различными типами данных.
\item Какие ограничения можно использовать с wildcard в Java generics? (1)
\begin{enumerate}
\item extends и super
\item only
\item extends
\end{enumerate}
% Неверные ответы: b) и c) - в generics для ограничения типа используются ключевые слова extends и super, а не only или extends.
\item Можно ли создать экземпляр обобщенного типа в Java, внутри класса, описывающего этот тип? (2)
\begin{enumerate}
\item Да, это возможно
\item Нет, такое создание экземпляров обобщенных типов запрещено
\item Это зависит от контекста использования обобщенного типа
\end{enumerate}
% Неверные ответы: a) и c) - экземпляры обобщенных типов не могут быть созданы в Java из-за стирания типов, однако можно создать экземпляр обобщенного класса, указав конкретный тип данных.
\end{enumerate}
\end{itemize}
\subsubsection*{Рассмотрение ДЗ}
\begin{itemize}
\item \textbf{Цель этапа:} Пояснить не очевидные моменты в формулировке ДЗ с лекции, синхронизировать прочитанный на лекции материал к началу семинара.
\item \textbf{Тайминг:} 15-20 минут.
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель демонстрирует свой вариант решения домашнего задания из лекции;
\item возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов.
\end{itemize}
\item \textbf{Домашнее задание из лекции:}
\begin{itemize}
\item Написать метод, который меняет два элемента массива местами (массив может быть любого ссылочного типа);
\textbf{Вариант решения}
Задание с подвохом, при решении необязательно было использовать дженерики, формулировка задания не ограничивает используемые в массиве типы, наоборот, явно проговаривается, что массив может быть любого типа.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static void swap(Object[] arr, int from, int to) {
Object temp = arr[from];
arr[from] = arr[to];
arr[to] = temp;
}
public static void main(String[] args) {
Object[] arr = {1, 2.0f, "hello"};
System.out.println(Arrays.toString(arr));
swap(arr, 0, 2);
System.out.println(Arrays.toString(arr));
}
\end{lstlisting}
\item Большая задача:
\begin{itemize}
\item Есть классы \code{Fruit} -> \code{Apple}, \code{Orange}; (больше не надо);
\item Класс \code{Box} в который можно складывать фрукты, коробки условно сортируются по типу фрукта, поэтому в одну коробку нельзя сложить и яблоки, и апельсины; Для хранения фруктов внутри коробки можете использовать \code{ArrayList};
\item Сделать метод \code{getWeight()} который высчитывает вес коробки, зная количество фруктов и вес одного фрукта(вес яблока -- \code{1.0f}, апельсина -- \code{1.5f}, не важно в каких единицах);
\item Внутри класса коробки сделать метод \code{compare()}, который позволяет сравнить текущую коробку с той, которую подадут в \code{compare()} в качестве параметра, \code{true} -- если их веса равны, \code{false} в противном случае (коробки с яблоками возможно сравнивать с коробками с апельсинами);
\item Написать метод, который позволяет пересыпать фрукты из текущей коробки в другую коробку (при этом, нельзя яблоки высыпать в коробку с апельсинами), соответственно, в текущей коробке фруктов не остается, а в другую перекидываются объекты, которые были в этой коробке.
\end{itemize}
\textbf{Вариант решения}
Создали фрукты с каким то разным весом, создали коробку, которая может вместить только фрукты. Надо фрукты там как то хранить, поэтому -- список. Соответственно при создании можем в нашу коробку какие то фрукты накидать. Соответственно метод, который будет уметь добавлять фрукты в коробку
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
private interface Fruit { float getWeight(); }
private static class Apple implements Fruit {
static final float weight = 1.0f;
@Override public float getWeight() { return weight; }
}
private static class Orange implements Fruit {
static final float weight = 1.5f;
@Override public float getWeight() { return weight; }
}
public static class Box<T extends Fruit> {
private final List<T> container;
Box(T[] init) {
container = new ArrayList<>();
for (T f : init) { add(f); }
}
void add(T fruit) { container.add(fruit); }
void print() { System.out.println(getWeight()); }
}
\end{lstlisting}
Поскольку в коробке у нас могут быть только одно типа фрукты -- мы можем не перебирать каждый из них, а умножить вес одного на количество.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
float getWeight() {
return (container.isEmpty())
? 0
: container.get(0).getWeight() * container.size();
}
\end{lstlisting}
Можно было, по большому счёту, переопределить метод equals() или написать свой метод compare() который принимает вторую коробку и сравнивает вес. Сравнивать можно любые фрукты, значит в методе не подходит параметр типа \code{<T>}
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
boolean compare(Box<?> with) {
return this.getWeight() - with.getWeight() < 0.0001;
}
\end{lstlisting}
Осталось только уметь пересыпать из одной коробки в другую не смешивая. На входе должна быть коробка того же типа, что и у объекта, и тут два пути, либо пересыпать всё из текущей коробки в другую, либо из другой в текущую, метод назван \code{transferTo()}, поэтому пересыпаться фрукты будут из текущей коробки. Возникает вопрос: если создаётся коробка с яблоками \code{Box<Apple>} и коробка с фруктами \code{Box<Fruit>}, должна ли быть возможность перекинуть из яблочной во фруктовую? Однозначно, нет. Поэтому, в аргументе метода используется \code{Box<? super T>}, таким образом яблоки возможно кидать в яблоки или в яблочных родителей, но не в апельсины.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
void transferTo(Box<? super T> dest) {
dest.container.addAll(container);
container.clear();
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Вопросы и ответы}
\begin{itemize}
\item \textbf{Ценность этапа} Вовлечение аудитории в обратную связь, пояснение неочевидных моментов в материале лекции и другой проделанной работе.
\item \textbf{Тайминг} 5-15 минут
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы;
\item Если преподаватель затрудняется с ответом, необходимо мягко предложить студенту ответить на его вопрос на следующем семинаре (и не забыть найти ответ на вопрос студента!);
\item Предложить и показать пути самостоятельного поиска студентом ответа на заданный вопрос;
\item Посоветовать литературу на тему заданного вопроса;
\item Дополнительно указать на то, что все сведения для выполнения домашнего задания, прохождения викторины и работы на семинаре были рассмотрены в методическом материале к этому или предыдущим урокам.
\end{itemize}
\end{itemize}
\subsubsection*{Задание 1}
\begin{itemize}
\item \textbf{Ценность этапа} Формирование навыков базовой работы с обобщениями.
\item \textbf{Тайминг} 10-15 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Пояснить студентам пользу и необходимость следования паттерну MVC, проговорить важность разделения логики и открывающиеся возможности при правильном проектировании.
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Создать обобщенный класс с тремя параметрами (\code{T}, \code{V}, \code{K}). Класс содержит три переменные типа (\code{T}, \code{V}, \code{K}), конструктор, принимающий на вход параметры типа (\code{T}, \code{V}, \code{K}), методы возвращающие значения трех переменных. Создать метод, выводящий на консоль имена классов для трех переменных класса. Наложить ограничения на параметры типа: \code{T} должен реализовать интерфейс \code{Comparable} (классы оболочки, \code{String}), \code{V} должен реализовать интерфейс \code{DataInput} и расширять класс \code{InputStream}, \code{K} должен расширять класс \code{Number}.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class MyClass<T extends Comparable, V extends InputStream & Serializable, K extends Number> {
T t; V v; K k;
MyClass(T t, V v, K k) {
this.t = t; this.v = v; this.k = k;
}
public T getT() { return t; }
public V getV() { return v; }
public K getK() { return k; }
void print() {
System.out.printf("t = %s, v = %s, k = %s",
t.getClass().getName(),
v.getClass().getName(),
k.getClass().getName());
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 2}
\begin{itemize}
\item \textbf{Ценность этапа} Практика написания обобщённого кода с дополнительным описанием алгоритмов манипулирования данными.
\item \textbf{Тайминг} 15-20 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Описать собственную коллекцию -- список на основе массива. Коллекция должна иметь возможность хранить любые типы данных, иметь методы добавления и удаления элементов.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class OwnList<T> {
Object[] arr;
int count;
OwnList() {
arr = new Object[1];
count = 0;
}
void add(T item) {
if (count == arr.length) {
Object[] newArr = new Object[arr.length * 2];
System.arraycopy(arr, 0, newArr, 0, arr.length);
arr = newArr;
}
arr[count++] = item;
}
T remove() {
if (count == 0) throw new NoSuchElementException();
T temp = (T) arr[--count];
arr[count] = null;
return temp;
}
@Override
public String toString() {
return Arrays.toString(arr);
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 3}
\begin{itemize}
\item \textbf{Ценность этапа} Изучение поведения итератора для понимания механизмов его действия и необходимости его использования в работе.
\item \textbf{Тайминг} 20-25 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Написать итератор по массиву. Итератор -- это объект, осуществляющий движение по коллекциям любого типа, содержащим любые типы данных. Итераторы обычно имеют только два метода -- проверка на наличие следующего элемента и переход к следующему элементу. Но также, особенно в других языках программирования, возможно встретить итераторы, реализующие дополнительную логику.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class ArrayIterator<T> {
private final T[] array;
private int index = 0;
public ArrayIterator(T[] array) {
this.array = array;
}
public boolean hasNext() {
return index < array.length;
}
public T next() {
if(!hasNext())
throw new NoSuchElementException();
return array[index++];
}
}
public static void main(String[] args) {
Integer[] arr = {1,2,3,4};
ArrayIterator<Integer> it = new ArrayIterator<>(arr);
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
}
\end{lstlisting}
\item [$*_1$] Написать итератор таким образом, чтобы его возможно было использовать для цикла \code{foreach}.
\textbf{Вариант решения}
Указать, что для этого достаточно реализовать интерфейс \code{Iterator<T>}.
\begin{lstlisting}[language=Java,style=JCodeStyle]
class ArrayIterator<T> implements Iterator<T>{
private T[] array;
private int index = 0;
public ArrayIterator(T[] array) {
this.array = array;
}
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public T next() {
if(!hasNext())
throw new NoSuchElementException();
return array[index++];
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 4}
\begin{itemize}
\item \textbf{Ценность этапа} Закрепление понимания механизмов работы коллекций (в том числе с применением итераторов).
\item \textbf{Тайминг} 20-25 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Описать интерфейс \code{Person} с методами \code{doWork()} и \code{haveRest()}. Написать два класса работник и бездельник, реализующих интерфейс. Работник работает, и не умеет бездельничать, в то время как бездельник не умеет работать, но умеет отдыхать. Написать обобщённые классы \code{Workplace} и \code{Club}, содержащие массив из \code{Person}. В классах необходимо вызывать у всего массива людей вызывать соответствующие методы.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
interface Person {
void doWork();
void haveRest();
}
private static class Worker implements Person {
@Override public void doWork() {
System.out.println("Work!"); }
@Override public void haveRest() {
System.out.println("What?");
}
}
private static class Idler implements Person {
@Override public void doWork() {
System.out.println("No!"); }
@Override public void haveRest() {
System.out.println("Chill!");
}
}
private static class Workplace<T extends Person> {
Person[] arr;
public Workplace(T... people) {
arr = people;
}
void work() {
for (Person person : arr) { person.doWork(); }
}
}
private static class Club<T extends Person> {
Person[] arr;
public Club(T... people) {
arr = people;
}
void chill() {
for (Person person : arr) { person.haveRest(); }
}
}
public static void main(String[] args) {
Workplace<Person> w = new Workplace<>(new Worker(), new Worker(), new Idler());
Club<Person> c = new Club<>(new Worker(), new Worker(), new Idler());
w.work();
c.chill();
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Домашнее задание}
\begin{itemize}
\item \textbf{Ценность этапа} Задать задание для самостоятельного выполнения между занятиями.
\item \textbf{Тайминг} 5-10 минут.
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Пояснить студентам в каком виде выполнять и сдавать задания
\item Уточнить кто будет проверять работы (преподаватель или ревьювер)
\item Объяснить к кому обращаться за помощью и где искать подсказки
\item Объяснить где взять проект заготовки для дз
\end{itemize}
\item \textbf{Задания}
\begin{enumerate}
\item [5-25 мин] Выполнить все задания семинара, если они не были решены, без ограничений по времени;
\textbf{Все варианты решения приведены в тексте семинара выше}
\item [25 мин] 1. Внедрить итератор из задания 2 в коллекцию, написанную в задании 3 таким образом, чтобы итератор был внутренним классом и, соответственно, объектом в коллекции.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class OList<T> implements Iterable<T> {
class ArrayIterator<T> implements Iterator<T> {
private int index = 0;
@Override
public boolean hasNext() {
return index < count;
}
@Override
public T next() {
if(!hasNext())
throw new NoSuchElementException();
return (T) arr[index++];
}
}
Object[] arr;
int count;
Main.OList.ArrayIterator iter;
OList() {
arr = new Object[1];
count = 0;
this.iter = new ArrayIterator<T>();
}
void add(T item) {
if (count == arr.length) {
Object[] newArr = new Object[arr.length * 2];
System.arraycopy(arr, 0, newArr, 0, arr.length);
arr = newArr;
}
arr[count++] = item;
}
T remove() {
if (count == 0) throw new NoSuchElementException();
--count;
T temp = (T) arr[count];
arr[count] = null;
return temp;
}
@Override
public Iterator<T> iterator() {
return iter;
}
@Override
public Spliterator<T> spliterator() {
return Iterable.super.spliterator();
}
@Override
public String toString() {
return Arrays.toString(arr);
}
}
\end{lstlisting}
\item [15 мин] 2. Написать класс Калькулятор (необобщенный), который содержит обобщенные статические методы: \code{sum()}, \code{multiply()}, \code{divide()}, \code{subtract()}. Параметры этих методов -- два \textbf{числа} разного типа, над которыми должна быть произведена операция.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class Calculator {
public static <T extends Number, U extends Number> double sum(T num1, U num2) {
return num1.doubleValue() + num2.doubleValue();
}
public static <T extends Number, U extends Number> double multiply(T num1, U num2) {
return num1.doubleValue() * num2.doubleValue();
}
public static <T extends Number, U extends Number> double divide(T num1, U num2) {
return num1.doubleValue() / num2.doubleValue();
}
public static <T extends Number, U extends Number> double subtract(T num1, U num2) {
return num1.doubleValue() - num2.doubleValue();
}
}
public static void main(String[] args) {
System.out.println(Calculator.sum(2, 2.0));
System.out.println(Calculator.multiply(2.0f, 2.0));
System.out.println(Calculator.divide(2L, 2.0));
System.out.println(Calculator.subtract(2, 2));
}
\end{lstlisting}
\item [10 мин] 3. Напишите обобщенный метод \code{compareArrays()}, который принимает два массива и возвращает \code{true}, если они одинаковые, и \code{false} в противном случае. Массивы могут быть любого типа данных, но должны иметь одинаковую длину и содержать элементы одного типа.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static <T> boolean compareArrays(T[] array1, T[] array2) {
if (array1.length != array2.length) {
return false;
}
for (int i = 0; i < array1.length; i++) {
if (!array1[i].equals(array2[i])) {
return false;
}
}
return true;
}
\end{lstlisting}
\item [10 мин] 4. Напишите обобщенный класс \code{Pair}, который представляет собой пару значений разного типа. Класс должен иметь методы \code{getFirst()}, \code{getSecond()} для получения значений пары, а также переопределение метода \code{toString()}, возвращающее строковое представление пары.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class Pair<T1, T2> {
private T1 first;
private T2 second;
public Pair(T1 first, T2 second) {
this.first = first;
this.second = second;
}
public T1 getFirst() { return first; }
public T2 getSecond() { return second; }
@Override public String toString() {
return "(" + first + ", " + second + ")";
}
}
public static void main(String[] args) {
Pair<Integer, String> pair1 = new Pair<>(1, "one");
System.out.println(pair1.getFirst()); // 1
System.out.println(pair1.getSecond()); // "one"
System.out.println(pair1); // "(1, one)"
Pair<String, Double> pair2 = new Pair<>("pi", 3.14);
System.out.println(pair2.getFirst()); // "pi"
System.out.println(pair2.getSecond()); // 3.14
System.out.println(pair2); // "(pi, 3.14)"
}
\end{lstlisting}
\end{enumerate}
\end{itemize}
\subsubsection*{Рефлексия и завершение семинара}
\begin{itemize}
\item \textbf{Цель этапа:} Привести урок к логическому завершению, посмотреть что студентам удалось, что было сложно и над чем нужно еще поработать
\item \textbf{Тайминг:} 5-10 минут
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Запросить обратную связь от студентов.
\item Подчеркните то, чему студенты научились на занятии.
\item Дайте рекомендации по решению заданий, если в этом есть необходимость
\item Дайте краткую обратную связь студентам.
\item Поделитесь ощущением от семинара.
\item Поблагодарите за проделанную работу.
\end{itemize}
\end{itemize}
\newpage
\end{document}

0
pdfit.sh Executable file → Normal file
View File

View File

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg156823"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="fon-neyman.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview156825"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="5.1241863"
inkscape:cx="298.87672"
inkscape:cy="328.8327"
inkscape:window-width="2560"
inkscape:window-height="1387"
inkscape:window-x="-8"
inkscape:window-y="22"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs156820" />
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.162934;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect156882"
width="33.044697"
height="13.526959"
x="34.130814"
y="76.729042" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="44.405422"
y="85.248093"
id="text158835"><tspan
id="tspan158833"
style="stroke-width:0.264583"
x="44.405422"
y="85.248093"
sodipodi:role="line">Ядро</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.162934;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect156882-8"
width="33.044697"
height="13.526959"
x="93.355263"
y="76.0578" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="102.39065"
y="84.267044"
id="text158835-1"><tspan
id="tspan158833-2"
style="stroke-width:0.264583"
x="102.39065"
y="84.267044"
sodipodi:role="line">Память</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 73.862744,78.303285 -6.505911,4.827799 7.142591,4.123778"
id="path187989" />
<path
style="fill:none;stroke:#000000;stroke-width:0.260797px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 86.788601,78.238058 6.32104,4.827799 -6.939628,4.123778"
id="path187989-1" />
<path
style="fill:none;stroke:#000000;stroke-width:0.261993px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 71.102229,80.523362 H 89.5374 l -0.0329,0.128592"
id="path188126" />
<path
style="fill:none;stroke:#000000;stroke-width:0.269623px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70.945409,85.041551 h 18.82067"
id="path188128" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="80.09272"
y="70.42907"
id="text194834"><tspan
sodipodi:role="line"
id="tspan194832"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="80.09272"
y="70.42907">шина</tspan><tspan
sodipodi:role="line"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="80.09272"
y="75.720734"
id="tspan194836">инструкций</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="80.497742"
y="91.826813"
id="text194834-4"><tspan
sodipodi:role="line"
id="tspan194832-4"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="80.497742"
y="91.826813">шина</tspan><tspan
sodipodi:role="line"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="80.497742"
y="97.118477"
id="tspan194836-3">данных</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="33.045898"
y="65.162376"
id="text207118"><tspan
sodipodi:role="line"
id="tspan207116"
style="stroke-width:0.264583"
x="33.045898"
y="65.162376">фон-Нейман</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

156
pics/01-ess-00-harvard.svg Normal file
View File

@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg212857"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="harvard.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview212859"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="3.6233469"
inkscape:cx="286.61346"
inkscape:cy="385.83112"
inkscape:window-width="2560"
inkscape:window-height="1387"
inkscape:window-x="-8"
inkscape:window-y="22"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs212854" />
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.201171;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect156882"
width="33.006458"
height="20.644859"
x="10.862647"
y="82.033134" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="21.118137"
y="90.533066"
id="text158835"><tspan
id="tspan158833"
style="stroke-width:0.264583"
x="21.118137"
y="90.533066"
sodipodi:role="line">Ядро</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.205017;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect156882-8"
width="33.002613"
height="21.444254"
x="70.08902"
y="81.363815" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="79.103363"
y="89.552017"
id="text158835-1"><tspan
id="tspan158833-2"
style="stroke-width:0.264583"
x="79.103363"
y="89.552017"
sodipodi:role="line">Память</tspan></text>
<g
id="g280624">
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 50.575459,83.588262 -6.505911,4.827799 7.142591,4.123778"
id="path187989" />
<path
style="fill:none;stroke:#000000;stroke-width:0.260797px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 63.501316,83.523035 6.32104,4.827799 -6.939628,4.123778"
id="path187989-1" />
<path
style="fill:none;stroke:#000000;stroke-width:0.261993px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 47.814944,85.808339 h 18.435171 l -0.0329,0.128592"
id="path188126" />
<path
style="fill:none;stroke:#000000;stroke-width:0.269623px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 47.658124,90.326528 h 18.82067"
id="path188128" />
</g>
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 50.371505,94.787991 -6.505911,4.827799 7.142591,4.12378"
id="path187989-5" />
<path
style="fill:none;stroke:#000000;stroke-width:0.260797px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 63.297362,94.722764 6.32104,4.827799 -6.939628,4.123777"
id="path187989-1-7" />
<path
style="fill:none;stroke:#000000;stroke-width:0.261993px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 47.61099,97.008068 h 18.435171 l -0.0329,0.128592"
id="path188126-7" />
<path
style="fill:none;stroke:#000000;stroke-width:0.269623px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 47.45417,101.52626 H 66.27484"
id="path188128-3" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="56.805435"
y="75.714043"
id="text194834"><tspan
sodipodi:role="line"
id="tspan194832"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="56.805435"
y="75.714043">шина</tspan><tspan
sodipodi:role="line"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="56.805435"
y="81.005707"
id="tspan194836">инструкций</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="56.918369"
y="108.43017"
id="text194834-4"><tspan
sodipodi:role="line"
id="tspan194832-4"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="56.918369"
y="108.43017">шина</tspan><tspan
sodipodi:role="line"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="56.918369"
y="113.72183"
id="tspan194836-3">данных</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
x="9.7586136"
y="70.44735"
id="text207118"><tspan
sodipodi:role="line"
id="tspan207116"
style="stroke-width:0.264583"
x="9.7586136"
y="70.44735">гарвард</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
pics/jd-01-btn-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

BIN
pics/jd-01-btn-02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

BIN
pics/jd-01-btn-03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

BIN
pics/jd-01-jframe-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

BIN
pics/jd-01-ttt-hum-turn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 KiB

BIN
pics/jd-01-ttt-rslt-cmp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
pics/jd-01-ttt-rslt-drw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
pics/jd-01-ttt-rslt-hum.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
pics/jd-02-adapter-impl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

BIN
pics/jd-03-38-01.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
pics/jd-03-38-02.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
pics/jd-03-38-03.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
pics/jd-03-38-04.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
pics/jd-03-38-05.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
pics/jd-03-38-06.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
pics/jd-03-38-08.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

285
pics/jd-04-04-one-proc.svg Normal file
View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-04-one-proc.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="0.88"
inkscape:cx="44.886364"
inkscape:cy="323.86364"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<rect
x="185"
y="325"
width="365"
height="205"
id="rect4879" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect4871" />
<linearGradient
inkscape:collect="always"
id="linearGradient3696">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop3692" />
<stop
style="stop-color:#ff0000;stop-opacity:0;"
offset="1"
id="stop3694" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3696"
id="linearGradient3698"
x1="50.052887"
y1="41.652685"
x2="125.65105"
y2="41.652685"
gradientUnits="userSpaceOnUse" />
<rect
x="185"
y="325"
width="365"
height="205"
id="rect4879-8" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect4871-9" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect5126" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect5128" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient3698);fill-opacity:1">
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="50.020206"
y="34.764507"
id="text447"><tspan
sodipodi:role="line"
id="tspan445"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="50.020206"
y="34.764507">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.742828"
y="40.034657"
id="text451"><tspan
sodipodi:role="line"
id="tspan449"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.742828"
y="40.034657">Процесс 3</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.835289"
y="44.934975"
id="text455"><tspan
sodipodi:role="line"
id="tspan453"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.835289"
y="44.934975">Процесс 2</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="50.020206"
id="text459"><tspan
sodipodi:role="line"
id="tspan457"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="50.020206">Процесс 1</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="54.828064"
id="text463"><tspan
sodipodi:role="line"
id="tspan461"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="54.828064">Процесс 0</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165;stroke-opacity:1;stroke-dasharray:none"
d="M 70.114583,55.5625 H 75.40625 V 43.65625 h 5.291667 v 5.291667 h 5.291666 V 38.364583 H 91.28125 V 55.5625 h 5.291667 V 43.65625 h 5.291663 v 5.291667 h 5.29167 V 38.364583 h 5.29167 v 5.291667 h 5.29166 v 5.291667 h 5.29167"
id="path553"
sodipodi:nodetypes="cccccccccccccccccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
x="64.822914"
y="30.427084"
id="text4693"><tspan
sodipodi:role="line"
id="tspan4691"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2"
x="64.822914"
y="30.427084">Процессор компьютера</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="116.41666"
y="59.53125"
id="text4747"><tspan
sodipodi:role="line"
id="tspan4745"
style="stroke-width:2;fill:#000000;fill-opacity:1"
x="116.41666"
y="59.53125">Время</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none;stroke:none;stroke-opacity:1"
x="124.35416"
y="48.947914"
id="text4751"><tspan
sodipodi:role="line"
id="tspan4749"
style="stroke-width:2;fill:#000000;stroke:none;stroke-opacity:1;fill-opacity:1"
x="124.35416"
y="48.947914">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="74.083336"
y="59.53125"
id="text4755"><tspan
sodipodi:role="line"
id="tspan4753"
style="stroke-width:2;fill:#000000;fill-opacity:1"
x="74.083336"
y="59.53125">вкл</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none;stroke:none"
x="82.020836"
y="62.177082"
id="text4865"><tspan
sodipodi:role="line"
id="tspan4863"
style="stroke-width:2;stroke:none;fill:#000000;fill-opacity:1"
x="82.020836"
y="62.177082">HyperThreading</tspan></text>
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 75.406249,43.656249 h 5.291667"
id="path6534" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 80.697916,48.947916 h 5.291666"
id="path6536" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 91.281249,55.562499 h 5.291666"
id="path6540" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 96.572915,43.656249 h 5.291665"
id="path6542" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 85.989583,38.364583 h 5.291665"
id="path6542-9" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114581,55.562499 h 5.291665"
id="path6542-3" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 101.86458,48.947916 h 5.29167"
id="path6544" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 107.15625,38.364583 h 5.29167"
id="path6546" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 112.44792,43.656249 h 5.29166"
id="path6548" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 117.73958,48.947916 h 5.29167"
id="path6550" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2);marker-start:url(#Arrow2)"
d="m 68.791666,33.072916 v 23.8125 l 55.562504,10e-7"
id="path7312" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.9 KiB

508
pics/jd-04-04-threading.svg Normal file
View File

@ -0,0 +1,508 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-04-threading.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="1.2445079"
inkscape:cx="412.61288"
inkscape:cy="169.54492"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<rect
x="185"
y="325"
width="365"
height="205"
id="rect4879" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect4871" />
<linearGradient
inkscape:collect="always"
id="linearGradient3696">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop3692" />
<stop
style="stop-color:#ff0000;stop-opacity:0;"
offset="1"
id="stop3694" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3696"
id="linearGradient3698"
x1="50.052887"
y1="41.652685"
x2="125.65105"
y2="41.652685"
gradientUnits="userSpaceOnUse" />
<rect
x="185"
y="325"
width="365"
height="205"
id="rect4879-8" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect4871-9" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect5126" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect5128" />
<marker
style="overflow:visible"
id="Arrow2-6"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L-6" />
</marker>
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient3698);fill-opacity:1">
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="50.020206"
y="34.764507"
id="text447"><tspan
sodipodi:role="line"
id="tspan445"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="50.020206"
y="34.764507">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.742828"
y="40.034657"
id="text451"><tspan
sodipodi:role="line"
id="tspan449"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.742828"
y="40.034657">Процесс 3</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.835289"
y="44.934975"
id="text455"><tspan
sodipodi:role="line"
id="tspan453"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.835289"
y="44.934975">Процесс 2</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="50.020206"
id="text459"><tspan
sodipodi:role="line"
id="tspan457"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="50.020206">Процесс 1</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="54.828064"
id="text463"><tspan
sodipodi:role="line"
id="tspan461"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="54.828064">Процесс 0</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165;stroke-dasharray:none;stroke-opacity:1"
d="M 70.114583,55.5625 H 75.40625 V 43.65625 h 5.291667 v 5.291667 h 5.291666 V 38.364583 H 91.28125 V 55.5625 h 5.291667 V 43.65625 h 5.291663 v 5.291667 h 5.29167 V 38.364583 h 5.29167 v 5.291667 h 5.29166 v 5.291667 h 5.29167"
id="path553"
sodipodi:nodetypes="cccccccccccccccccccc" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114583,48.947917 h 5.291667 l 0,6.614583 h 5.291667 v 0 h 5.291666 l 0,0 h 5.291667 v -6.614583 h 5.291667 l 0,-10.583334 h 5.291663 l 0,0 h 5.29167 l 0,10.583334 h 5.29167 l 0,6.614583 h 5.29166 v 0 h 3.96875"
id="path553-8"
sodipodi:nodetypes="cccccccccccccccccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
x="64.822914"
y="30.427084"
id="text4693"><tspan
sodipodi:role="line"
id="tspan4691"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2"
x="64.822914"
y="30.427084">Процессор компьютера</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="116.41666"
y="59.53125"
id="text4747"><tspan
sodipodi:role="line"
id="tspan4745"
style="stroke-width:2;fill:#000000;fill-opacity:1"
x="116.41666"
y="59.53125">Время</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none;stroke:none;stroke-opacity:1"
x="124.35416"
y="48.947914"
id="text4751"><tspan
sodipodi:role="line"
id="tspan4749"
style="stroke-width:2;fill:#000000;stroke:none;stroke-opacity:1;fill-opacity:1"
x="124.35416"
y="48.947914">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="74.083336"
y="59.53125"
id="text4755"><tspan
sodipodi:role="line"
id="tspan4753"
style="stroke-width:2;fill:#000000;fill-opacity:1"
x="74.083336"
y="59.53125">вкл</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none;stroke:none"
x="82.020836"
y="62.177082"
id="text4865"><tspan
sodipodi:role="line"
id="tspan4863"
style="stroke-width:2;stroke:none;fill:#000000;fill-opacity:1"
x="82.020836"
y="62.177082">HyperThreading</tspan></text>
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 75.406249,43.656249 h 5.291667"
id="path6534" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 80.697916,48.947916 h 5.291666"
id="path6536" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 91.281249,55.562499 h 5.291666"
id="path6540" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 96.572915,43.656249 h 5.291665"
id="path6542" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 85.989583,38.364583 h 5.291665"
id="path6542-9" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114581,55.562499 h 5.291665"
id="path6542-3" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 101.86458,48.947916 h 5.29167"
id="path6544" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 107.15625,38.364583 h 5.29167"
id="path6546" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 112.44792,43.656249 h 5.29166"
id="path6548" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 117.73958,48.947916 h 5.29167"
id="path6550" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2);marker-end:url(#Arrow2)"
d="m 68.791667,33.072917 v 23.8125 l 55.562503,1e-6"
id="path7312"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114582,48.947916 h 5.291667"
id="path7496" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1"
d="m 75.406249,55.562499 h 15.875"
id="path7498" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 91.281249,48.947916 h 5.291666"
id="path7500" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="M 96.572915,38.364583 H 107.15625"
id="path7502" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 107.15625,48.947916 h 5.29167"
id="path7504" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 112.44792,55.562499 h 10.58333"
id="path7506" />
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.99999"
y="19.84375"
id="text447-9"><tspan
sodipodi:role="line"
id="tspan445-0"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.99999"
y="19.84375">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.72261"
y="25.113899"
id="text451-8"><tspan
sodipodi:role="line"
id="tspan449-8"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.72261"
y="25.113899">Поток 3</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.81507"
y="30.014217"
id="text455-3"><tspan
sodipodi:role="line"
id="tspan453-1"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.81507"
y="30.014217">Поток 2</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.90753"
y="35.099449"
id="text459-1"><tspan
sodipodi:role="line"
id="tspan457-0"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.90753"
y="35.099449">Поток 1</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.90753"
y="39.90731"
id="text463-3"><tspan
sodipodi:role="line"
id="tspan461-0"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
x="126.90753"
y="39.90731">Поток 0</tspan></text>
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165;stroke-dasharray:none;stroke-opacity:1"
d="m 141.55207,41.010417 h 5.29167 v -11.90625 h 5.29167 v 5.291667 h 5.29166 V 23.8125 h 5.29167 v 17.197917 h 5.29167 v -11.90625 h 5.29166 v 5.291667 h 5.29167 V 23.8125 h 5.29167 v 5.291667 h 5.29166 v 5.291667 h 5.29167"
id="path553-5"
sodipodi:nodetypes="cccccccccccccccccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
x="136.26042"
y="15.875"
id="text4693-4"><tspan
sodipodi:role="line"
id="tspan4691-7"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2"
x="136.26042"
y="15.875">Один процесс с системе</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="187.85416"
y="44.979164"
id="text4747-2"><tspan
sodipodi:role="line"
id="tspan4745-6"
style="fill:#000000;fill-opacity:1;stroke-width:2"
x="187.85416"
y="44.979164">Время</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
x="195.79166"
y="34.395828"
id="text4751-6"><tspan
sodipodi:role="line"
id="tspan4749-4"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-opacity:1"
x="195.79166"
y="34.395828">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="140.22917"
y="44.979164"
id="text4755-8"><tspan
sodipodi:role="line"
id="tspan4753-7"
style="fill:#000000;fill-opacity:1;stroke-width:2"
x="140.22917"
y="44.979164">старт</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
x="154.78125"
y="47.625"
id="text4865-1"><tspan
sodipodi:role="line"
id="tspan4863-2"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2"
x="154.78125"
y="47.625">Многопоточность</tspan></text>
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 146.84374,29.104166 h 5.29167"
id="path6534-7" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 152.13541,34.395833 h 5.29166"
id="path6536-7" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 162.71874,41.010416 h 5.29167"
id="path6540-5" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 168.01041,29.104166 h 5.29166"
id="path6542-5" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 157.42707,23.8125 h 5.29167"
id="path6542-9-7" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 141.55207,41.010416 h 5.29167"
id="path6542-3-4" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 173.30207,34.395833 h 5.29167"
id="path6544-3" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 178.59374,23.8125 h 5.29167"
id="path6546-9" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 183.88541,29.104166 h 5.29166"
id="path6548-4" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 189.17707,34.395833 h 5.29167"
id="path6550-3" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2-6);marker-end:url(#Arrow2-6)"
d="m 140.22916,18.520833 v 23.8125 l 55.5625,10e-7"
id="path7312-1" />
<ellipse
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-dasharray:1.80000007,1.80000007;stroke-opacity:1;stroke-dashoffset:0"
id="path9450"
cx="99.21875"
cy="38.364582"
rx="15.875"
ry="2.6458333" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-dasharray:1.8, 1.8;stroke-dashoffset:0;stroke-opacity:1"
d="m 113.77083,39.6875 26.45833,6.614583"
id="path9452" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-dasharray:1.8, 1.8;stroke-dashoffset:0;stroke-opacity:1"
d="M 103.1875,35.71875 127,14.552083"
id="path9454" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

315
pics/jd-04-04-two-proc.svg Normal file
View File

@ -0,0 +1,315 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-04-two-proc.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="4.9780317"
inkscape:cx="327.94086"
inkscape:cy="175.5714"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<rect
x="185"
y="325"
width="365"
height="205"
id="rect4879" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect4871" />
<linearGradient
inkscape:collect="always"
id="linearGradient3696">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop3692" />
<stop
style="stop-color:#ff0000;stop-opacity:0;"
offset="1"
id="stop3694" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3696"
id="linearGradient3698"
x1="50.052887"
y1="41.652685"
x2="125.65105"
y2="41.652685"
gradientUnits="userSpaceOnUse" />
<rect
x="185"
y="325"
width="365"
height="205"
id="rect4879-8" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect4871-9" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect5126" />
<rect
x="160"
y="80"
width="410"
height="175"
id="rect5128" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline;fill:url(#linearGradient3698);fill-opacity:1">
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="50.020206"
y="34.764507"
id="text447"><tspan
sodipodi:role="line"
id="tspan445"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="50.020206"
y="34.764507">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.742828"
y="40.034657"
id="text451"><tspan
sodipodi:role="line"
id="tspan449"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.742828"
y="40.034657">Процесс 3</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.835289"
y="44.934975"
id="text455"><tspan
sodipodi:role="line"
id="tspan453"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.835289"
y="44.934975">Процесс 2</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="50.020206"
id="text459"><tspan
sodipodi:role="line"
id="tspan457"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="50.020206">Процесс 1</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="54.828064"
id="text463"><tspan
sodipodi:role="line"
id="tspan461"
style="fill:#000000;fill-opacity:1;stroke-width:0.264583;stroke:none;stroke-opacity:1"
x="49.927746"
y="54.828064">Процесс 0</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165;stroke-dasharray:none;stroke-opacity:1"
d="M 70.114583,55.5625 H 75.40625 V 43.65625 h 5.291667 v 5.291667 h 5.291666 V 38.364583 H 91.28125 V 55.5625 h 5.291667 V 43.65625 h 5.291663 v 5.291667 h 5.29167 V 38.364583 h 5.29167 v 5.291667 h 5.29166 v 5.291667 h 5.29167"
id="path553"
sodipodi:nodetypes="cccccccccccccccccccc" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114583,48.947917 h 5.291667 l 0,6.614583 h 5.291667 v 0 h 5.291666 l 0,0 h 5.291667 v -6.614583 h 5.291667 l 0,-10.583334 h 5.291663 l 0,0 h 5.29167 l 0,10.583334 h 5.29167 l 0,6.614583 h 5.29166 v 0 h 3.96875"
id="path553-8"
sodipodi:nodetypes="cccccccccccccccccccc" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
x="64.822914"
y="30.427084"
id="text4693"><tspan
sodipodi:role="line"
id="tspan4691"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2"
x="64.822914"
y="30.427084">Процессор компьютера</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="116.41666"
y="59.53125"
id="text4747"><tspan
sodipodi:role="line"
id="tspan4745"
style="stroke-width:2;fill:#000000;fill-opacity:1"
x="116.41666"
y="59.53125">Время</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none;stroke:none;stroke-opacity:1"
x="124.35416"
y="48.947914"
id="text4751"><tspan
sodipodi:role="line"
id="tspan4749"
style="stroke-width:2;fill:#000000;stroke:none;stroke-opacity:1;fill-opacity:1"
x="124.35416"
y="48.947914">...</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="74.083336"
y="59.53125"
id="text4755"><tspan
sodipodi:role="line"
id="tspan4753"
style="stroke-width:2;fill:#000000;fill-opacity:1"
x="74.083336"
y="59.53125">вкл</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke-width:2;stroke-dasharray:none;stroke:none"
x="82.020836"
y="62.177082"
id="text4865"><tspan
sodipodi:role="line"
id="tspan4863"
style="stroke-width:2;stroke:none;fill:#000000;fill-opacity:1"
x="82.020836"
y="62.177082">HyperThreading</tspan></text>
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 75.406249,43.656249 h 5.291667"
id="path6534" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 80.697916,48.947916 h 5.291666"
id="path6536" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 91.281249,55.562499 h 5.291666"
id="path6540" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 96.572915,43.656249 h 5.291665"
id="path6542" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 85.989583,38.364583 h 5.291665"
id="path6542-9" />
<path
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114581,55.562499 h 5.291665"
id="path6542-3" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 101.86458,48.947916 h 5.29167"
id="path6544" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 107.15625,38.364583 h 5.29167"
id="path6546" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 112.44792,43.656249 h 5.29166"
id="path6548" />
<path
style="fill:none;fill-opacity:1;stroke-width:1;stroke-dasharray:none;stroke:#000000;stroke-opacity:1"
d="m 117.73958,48.947916 h 5.29167"
id="path6550" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2);marker-end:url(#Arrow2)"
d="m 68.791667,33.072917 v 23.8125 l 55.562503,1e-6"
id="path7312"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 70.114582,48.947916 h 5.291667"
id="path7496" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1"
d="m 75.406249,55.562499 h 15.875"
id="path7498" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 91.281249,48.947916 h 5.291666"
id="path7500" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="M 96.572915,38.364583 H 107.15625"
id="path7502" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 107.15625,48.947916 h 5.29167"
id="path7504" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-dasharray:none;stroke-opacity:1"
d="m 112.44792,55.562499 h 10.58333"
id="path7506" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

119
pics/jd-04-05-0.svg Normal file
View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-0.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

130
pics/jd-04-05-1.svg Normal file
View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-1.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="84.666664"
y="30.427084"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="84.666664"
y="30.427084">1</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

383
pics/jd-04-05-10.svg Normal file
View File

@ -0,0 +1,383 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-10.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect15790"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect15754"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15750"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15746"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15742"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15738"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15734"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15730"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15726"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="46.302082"
y="27.78125"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="46.302082"
y="27.78125">10</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect15722"
width="2.6458333"
height="10.583333"
x="33.072918"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,17.197916 c 0.441238,0 0.882208,0 1.322917,0"
id="path15724"
inkscape:path-effect="#path-effect15726"
inkscape:original-d="m 33.072916,17.197916 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,18.520833 c 0.441238,0 0.882208,0 1.322917,0"
id="path15728"
inkscape:path-effect="#path-effect15730"
inkscape:original-d="m 33.072916,18.520833 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,19.84375 c 0.441238,0 0.882208,0 1.322917,0"
id="path15732"
inkscape:path-effect="#path-effect15734"
inkscape:original-d="m 33.072916,19.84375 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,21.166666 c 0.441238,0 0.882208,0 1.322917,0"
id="path15736"
inkscape:path-effect="#path-effect15738"
inkscape:original-d="m 33.072916,21.166666 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,22.489583 c 0.441238,0 0.882208,0 1.322917,0"
id="path15740"
inkscape:path-effect="#path-effect15742"
inkscape:original-d="m 33.072916,22.489583 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,23.8125 c 0.441238,0 0.882208,0 1.322917,0"
id="path15744"
inkscape:path-effect="#path-effect15746"
inkscape:original-d="m 33.072916,23.8125 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,25.135416 c 0.441238,0 0.882208,0 1.322917,0"
id="path15748"
inkscape:path-effect="#path-effect15750"
inkscape:original-d="m 33.072916,25.135416 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,17.197916 c 0.441033,2.205167 0.882006,4.410027 0.66146,6.173808 -0.220546,1.76378 -1.102473,3.086671 -1.763885,2.645798 -0.661412,-0.440872 -1.102375,-2.645689 -0.881937,-4.630181 0.220438,-1.984493 1.102365,-3.748347 1.984362,-5.512341"
id="path15752"
inkscape:path-effect="#path-effect15754"
inkscape:original-d="m 38.364583,17.197916 c 0.441238,2.205126 0.882208,4.409987 1.322917,6.614584 -0.881698,1.323207 -1.763626,2.646098 -2.645834,3.96875 -0.440716,-2.204641 -0.881679,-4.409458 -1.322916,-6.614584 0.882226,-1.763659 1.764154,-3.527513 2.645833,-5.291666" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="29.104166"
y="29.104166"
id="text15784"><tspan
sodipodi:role="line"
id="tspan15782"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="29.104166">Event</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="33.072914"
id="tspan15786">Queue</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="M 44.979167,38.364583 C 47.183983,34.836877 49.388844,31.309099 51.59375,27.78125"
id="path15788"
sodipodi:nodetypes="cc"
inkscape:original-d="M 44.979167,38.364583 C 47.184292,34.83707 49.389153,31.309293 51.59375,27.78125"
inkscape:path-effect="#path-effect15790" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="39.6875"
y="41.010418"
id="text17406"><tspan
sodipodi:role="line"
id="tspan17404"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="41.010418">Override</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="44.979168"
id="tspan17408">Action Performed</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

393
pics/jd-04-05-11.svg Normal file
View File

@ -0,0 +1,393 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-11.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect22011"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15790"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect15754"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15750"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15746"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15742"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15738"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15734"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15730"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15726"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="48.947918"
y="21.166666"
id="text11985"><tspan
sodipodi:role="line"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="48.947918"
y="21.166666"
id="tspan22003">11</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect15722"
width="2.6458333"
height="10.583333"
x="33.072918"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,17.197916 c 0.441238,0 0.882208,0 1.322917,0"
id="path15724"
inkscape:path-effect="#path-effect15726"
inkscape:original-d="m 33.072916,17.197916 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,18.520833 c 0.441238,0 0.882208,0 1.322917,0"
id="path15728"
inkscape:path-effect="#path-effect15730"
inkscape:original-d="m 33.072916,18.520833 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,19.84375 c 0.441238,0 0.882208,0 1.322917,0"
id="path15732"
inkscape:path-effect="#path-effect15734"
inkscape:original-d="m 33.072916,19.84375 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,21.166666 c 0.441238,0 0.882208,0 1.322917,0"
id="path15736"
inkscape:path-effect="#path-effect15738"
inkscape:original-d="m 33.072916,21.166666 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,22.489583 c 0.441238,0 0.882208,0 1.322917,0"
id="path15740"
inkscape:path-effect="#path-effect15742"
inkscape:original-d="m 33.072916,22.489583 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,23.8125 c 0.441238,0 0.882208,0 1.322917,0"
id="path15744"
inkscape:path-effect="#path-effect15746"
inkscape:original-d="m 33.072916,23.8125 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,25.135416 c 0.441238,0 0.882208,0 1.322917,0"
id="path15748"
inkscape:path-effect="#path-effect15750"
inkscape:original-d="m 33.072916,25.135416 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,17.197916 c 0.441033,2.205167 0.882006,4.410027 0.66146,6.173808 -0.220546,1.76378 -1.102473,3.086671 -1.763885,2.645798 -0.661412,-0.440872 -1.102375,-2.645689 -0.881937,-4.630181 0.220438,-1.984493 1.102365,-3.748347 1.984362,-5.512341"
id="path15752"
inkscape:path-effect="#path-effect15754"
inkscape:original-d="m 38.364583,17.197916 c 0.441238,2.205126 0.882208,4.409987 1.322917,6.614584 -0.881698,1.323207 -1.763626,2.646098 -2.645834,3.96875 -0.440716,-2.204641 -0.881679,-4.409458 -1.322916,-6.614584 0.882226,-1.763659 1.764154,-3.527513 2.645833,-5.291666" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="29.104166"
y="29.104166"
id="text15784"><tspan
sodipodi:role="line"
id="tspan15782"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="29.104166">Event</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="33.072914"
id="tspan15786">Queue</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="39.6875"
y="41.010418"
id="text17406"><tspan
sodipodi:role="line"
id="tspan17404"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="41.010418">Override</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="44.979168"
id="tspan17408">Action Performed</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 44.979166,22.489583 c 0,-0.88168 0,-1.763624 0,-2.645833"
id="path22009"
inkscape:path-effect="#path-effect22011"
inkscape:original-d="m 44.979166,22.489583 c 2.65e-4,-0.88168 2.65e-4,-1.763624 0,-2.645833" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

130
pics/jd-04-05-2.svg Normal file
View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-2.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="88.635414"
y="34.395832"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="88.635414"
y="34.395832">2</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

130
pics/jd-04-05-3.svg Normal file
View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-3.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="79.375"
y="23.8125"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="79.375"
y="23.8125">3</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

130
pics/jd-04-05-4.svg Normal file
View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-4.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="66.145836"
y="37.041668"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="66.145836"
y="37.041668">4</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

171
pics/jd-04-05-5.svg Normal file
View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-5.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="62.177082"
y="18.520834"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="62.177082"
y="18.520834">5</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

366
pics/jd-04-05-6.svg Normal file
View File

@ -0,0 +1,366 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-6.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect15790"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect15754"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15750"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15746"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15742"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15738"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15734"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15730"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15726"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="39.6875"
y="17.197916"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="39.6875"
y="17.197916">6</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect15722"
width="2.6458333"
height="10.583333"
x="33.072918"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,17.197916 c 0.441238,0 0.882208,0 1.322917,0"
id="path15724"
inkscape:path-effect="#path-effect15726"
inkscape:original-d="m 33.072916,17.197916 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,18.520833 c 0.441238,0 0.882208,0 1.322917,0"
id="path15728"
inkscape:path-effect="#path-effect15730"
inkscape:original-d="m 33.072916,18.520833 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,19.84375 c 0.441238,0 0.882208,0 1.322917,0"
id="path15732"
inkscape:path-effect="#path-effect15734"
inkscape:original-d="m 33.072916,19.84375 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,21.166666 c 0.441238,0 0.882208,0 1.322917,0"
id="path15736"
inkscape:path-effect="#path-effect15738"
inkscape:original-d="m 33.072916,21.166666 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,22.489583 c 0.441238,0 0.882208,0 1.322917,0"
id="path15740"
inkscape:path-effect="#path-effect15742"
inkscape:original-d="m 33.072916,22.489583 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,23.8125 c 0.441238,0 0.882208,0 1.322917,0"
id="path15744"
inkscape:path-effect="#path-effect15746"
inkscape:original-d="m 33.072916,23.8125 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,25.135416 c 0.441238,0 0.882208,0 1.322917,0"
id="path15748"
inkscape:path-effect="#path-effect15750"
inkscape:original-d="m 33.072916,25.135416 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,17.197916 c 0.441033,2.205167 0.882006,4.410027 0.66146,6.173808 -0.220546,1.76378 -1.102473,3.086671 -1.763885,2.645798 -0.661412,-0.440872 -1.102375,-2.645689 -0.881937,-4.630181 0.220438,-1.984493 1.102365,-3.748347 1.984362,-5.512341"
id="path15752"
inkscape:path-effect="#path-effect15754"
inkscape:original-d="m 38.364583,17.197916 c 0.441238,2.205126 0.882208,4.409987 1.322917,6.614584 -0.881698,1.323207 -1.763626,2.646098 -2.645834,3.96875 -0.440716,-2.204641 -0.881679,-4.409458 -1.322916,-6.614584 0.882226,-1.763659 1.764154,-3.527513 2.645833,-5.291666" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="29.104166"
y="29.104166"
id="text15784"><tspan
sodipodi:role="line"
id="tspan15782"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="29.104166">Event</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="33.072914"
id="tspan15786">Queue</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="M 43.656249,21.166666 C 42.333638,20.284925 41.010722,19.402981 39.6875,18.520833"
id="path15788"
inkscape:path-effect="#path-effect15790"
inkscape:original-d="M 43.656249,21.166666 C 42.333597,20.284987 41.010681,19.403042 39.6875,18.520833" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

367
pics/jd-04-05-7.svg Normal file
View File

@ -0,0 +1,367 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-7.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect15790"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect15754"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15750"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15746"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15742"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15738"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15734"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15730"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15726"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="46.302082"
y="27.78125"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="46.302082"
y="27.78125">7</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect15722"
width="2.6458333"
height="10.583333"
x="33.072918"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,17.197916 c 0.441238,0 0.882208,0 1.322917,0"
id="path15724"
inkscape:path-effect="#path-effect15726"
inkscape:original-d="m 33.072916,17.197916 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,18.520833 c 0.441238,0 0.882208,0 1.322917,0"
id="path15728"
inkscape:path-effect="#path-effect15730"
inkscape:original-d="m 33.072916,18.520833 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,19.84375 c 0.441238,0 0.882208,0 1.322917,0"
id="path15732"
inkscape:path-effect="#path-effect15734"
inkscape:original-d="m 33.072916,19.84375 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,21.166666 c 0.441238,0 0.882208,0 1.322917,0"
id="path15736"
inkscape:path-effect="#path-effect15738"
inkscape:original-d="m 33.072916,21.166666 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,22.489583 c 0.441238,0 0.882208,0 1.322917,0"
id="path15740"
inkscape:path-effect="#path-effect15742"
inkscape:original-d="m 33.072916,22.489583 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,23.8125 c 0.441238,0 0.882208,0 1.322917,0"
id="path15744"
inkscape:path-effect="#path-effect15746"
inkscape:original-d="m 33.072916,23.8125 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,25.135416 c 0.441238,0 0.882208,0 1.322917,0"
id="path15748"
inkscape:path-effect="#path-effect15750"
inkscape:original-d="m 33.072916,25.135416 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,17.197916 c 0.441033,2.205167 0.882006,4.410027 0.66146,6.173808 -0.220546,1.76378 -1.102473,3.086671 -1.763885,2.645798 -0.661412,-0.440872 -1.102375,-2.645689 -0.881937,-4.630181 0.220438,-1.984493 1.102365,-3.748347 1.984362,-5.512341"
id="path15752"
inkscape:path-effect="#path-effect15754"
inkscape:original-d="m 38.364583,17.197916 c 0.441238,2.205126 0.882208,4.409987 1.322917,6.614584 -0.881698,1.323207 -1.763626,2.646098 -2.645834,3.96875 -0.440716,-2.204641 -0.881679,-4.409458 -1.322916,-6.614584 0.882226,-1.763659 1.764154,-3.527513 2.645833,-5.291666" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="29.104166"
y="29.104166"
id="text15784"><tspan
sodipodi:role="line"
id="tspan15782"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="29.104166">Event</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="33.072914"
id="tspan15786">Queue</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,30.427083 c 2.204998,-0.881999 4.409859,-1.763943 6.614584,-2.645833"
id="path15788"
inkscape:path-effect="#path-effect15790"
inkscape:original-d="m 38.364583,30.427083 c 2.205126,-0.881679 4.409987,-1.763624 6.614584,-2.645833"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

376
pics/jd-04-05-8.svg Normal file
View File

@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-8.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect15790"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect15754"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15750"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15746"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15742"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15738"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15734"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15730"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15726"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="46.302082"
y="27.78125"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="46.302082"
y="27.78125">8</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect15722"
width="2.6458333"
height="10.583333"
x="33.072918"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,17.197916 c 0.441238,0 0.882208,0 1.322917,0"
id="path15724"
inkscape:path-effect="#path-effect15726"
inkscape:original-d="m 33.072916,17.197916 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,18.520833 c 0.441238,0 0.882208,0 1.322917,0"
id="path15728"
inkscape:path-effect="#path-effect15730"
inkscape:original-d="m 33.072916,18.520833 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,19.84375 c 0.441238,0 0.882208,0 1.322917,0"
id="path15732"
inkscape:path-effect="#path-effect15734"
inkscape:original-d="m 33.072916,19.84375 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,21.166666 c 0.441238,0 0.882208,0 1.322917,0"
id="path15736"
inkscape:path-effect="#path-effect15738"
inkscape:original-d="m 33.072916,21.166666 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,22.489583 c 0.441238,0 0.882208,0 1.322917,0"
id="path15740"
inkscape:path-effect="#path-effect15742"
inkscape:original-d="m 33.072916,22.489583 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,23.8125 c 0.441238,0 0.882208,0 1.322917,0"
id="path15744"
inkscape:path-effect="#path-effect15746"
inkscape:original-d="m 33.072916,23.8125 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,25.135416 c 0.441238,0 0.882208,0 1.322917,0"
id="path15748"
inkscape:path-effect="#path-effect15750"
inkscape:original-d="m 33.072916,25.135416 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,17.197916 c 0.441033,2.205167 0.882006,4.410027 0.66146,6.173808 -0.220546,1.76378 -1.102473,3.086671 -1.763885,2.645798 -0.661412,-0.440872 -1.102375,-2.645689 -0.881937,-4.630181 0.220438,-1.984493 1.102365,-3.748347 1.984362,-5.512341"
id="path15752"
inkscape:path-effect="#path-effect15754"
inkscape:original-d="m 38.364583,17.197916 c 0.441238,2.205126 0.882208,4.409987 1.322917,6.614584 -0.881698,1.323207 -1.763626,2.646098 -2.645834,3.96875 -0.440716,-2.204641 -0.881679,-4.409458 -1.322916,-6.614584 0.882226,-1.763659 1.764154,-3.527513 2.645833,-5.291666" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="29.104166"
y="29.104166"
id="text15784"><tspan
sodipodi:role="line"
id="tspan15782"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="29.104166">Event</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="33.072914"
id="tspan15786">Queue</tspan></text>
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="39.6875"
y="41.010418"
id="text17406"><tspan
sodipodi:role="line"
id="tspan17404"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="41.010418">Override</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="44.979168"
id="tspan17408">Action Performed</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

383
pics/jd-04-05-9.svg Normal file
View File

@ -0,0 +1,383 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg10302"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="09-05-9.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10304"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.3109025"
inkscape:cx="233.32007"
inkscape:cy="130.17599"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid10681" />
</sodipodi:namedview>
<defs
id="defs10299">
<inkscape:path-effect
effect="bspline"
id="path-effect15790"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<marker
style="overflow:visible"
id="Arrow2"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="Arrow2"
markerWidth="7.6999998"
markerHeight="5.5999999"
viewBox="0 0 7.7 5.6"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="arrow2L" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect15754"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15750"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15746"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15742"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15738"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15734"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15730"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect15726"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14971"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect14967"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11239"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect11231"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect10735"
width="29.104166"
height="18.520834"
x="42.333332"
y="13.229166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 56.885416,31.75 v 3.96875"
id="path11217" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 48.947916,35.71875 h 15.875"
id="path11219" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="rect11221"
width="9.2604265"
height="22.489584"
x="72.760414"
y="13.229167" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
id="path11223"
cx="89.958336"
cy="31.75"
rx="2.6458333"
ry="3.96875" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 87.312499,31.75 h 5.291667"
id="path11225" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="M 89.958332,31.75 V 27.78125"
id="path11227" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 82.020832,25.135416 c 0.882049,-0.441024 1.763996,-0.881997 2.204915,-0.661344 0.440919,0.220654 0.440919,1.102581 0.881953,1.322904 0.441033,0.220323 1.322961,-0.220641 1.76388,1.3e-5 0.440919,0.220654 0.440919,1.102582 0.881953,1.322904 0.441034,0.220323 1.322961,-0.220641 1.76388,-0.220477 0.440919,1.63e-4 0.440919,0.441125 0.440919,0.881834"
id="path11229"
inkscape:path-effect="#path-effect11231"
inkscape:original-d="m 82.020832,25.135416 c 0.882208,-0.440707 1.764155,-0.881679 2.645834,-1.322916 2.64e-4,0.882226 2.64e-4,1.764153 0,2.645833 0.882226,-0.440716 1.764154,-0.88168 2.645833,-1.322917 2.64e-4,0.882227 2.64e-4,1.764155 0,2.645834 0.882227,-0.440717 1.764154,-0.881679 2.645833,-1.322917 2.65e-4,0.441246 2.65e-4,0.882208 0,1.322917" />
<path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-dasharray:none"
d="m 72.760416,34.395833 c -1.322855,0.881903 -2.645773,1.763848 -3.307261,1.543502 -0.661489,-0.220347 -0.661489,-1.543238 -1.32293,-1.763868 -0.661441,-0.220629 -1.984332,0.661298 -2.64582,0.440951 C 64.822916,34.396071 64.822916,33.073181 64.822916,31.75"
id="path11237"
inkscape:path-effect="#path-effect11239"
inkscape:original-d="m 72.760416,34.395833 c -1.322652,0.882208 -2.645569,1.764154 -3.96875,2.645833 2.64e-4,-1.322678 2.64e-4,-2.645569 0,-3.96875 -1.322679,0.882227 -2.645569,1.764155 -3.96875,2.645834 2.64e-4,-1.322679 2.64e-4,-2.645569 0,-3.96875" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none"
x="46.302082"
y="27.78125"
id="text11985"><tspan
sodipodi:role="line"
id="tspan11983"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2"
x="46.302082"
y="27.78125">9</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect14963"
width="17.197916"
height="13.229166"
x="43.65625"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 43.656249,18.520833 c 5.732905,0 11.465542,0 17.197917,0"
id="path14965"
inkscape:path-effect="#path-effect14967"
inkscape:original-d="m 43.656249,18.520833 c 5.732905,2.65e-4 11.465542,2.65e-4 17.197917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 58.208333,15.875 c 0,0.882209 0,1.764153 0,2.645833"
id="path14969"
inkscape:path-effect="#path-effect14971"
inkscape:original-d="m 58.208333,15.875 c 2.64e-4,0.882209 2.64e-4,1.764153 0,2.645833" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
id="rect15722"
width="2.6458333"
height="10.583333"
x="33.072918"
y="15.875" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,17.197916 c 0.441238,0 0.882208,0 1.322917,0"
id="path15724"
inkscape:path-effect="#path-effect15726"
inkscape:original-d="m 33.072916,17.197916 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,18.520833 c 0.441238,0 0.882208,0 1.322917,0"
id="path15728"
inkscape:path-effect="#path-effect15730"
inkscape:original-d="m 33.072916,18.520833 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,19.84375 c 0.441238,0 0.882208,0 1.322917,0"
id="path15732"
inkscape:path-effect="#path-effect15734"
inkscape:original-d="m 33.072916,19.84375 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,21.166666 c 0.441238,0 0.882208,0 1.322917,0"
id="path15736"
inkscape:path-effect="#path-effect15738"
inkscape:original-d="m 33.072916,21.166666 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,22.489583 c 0.441238,0 0.882208,0 1.322917,0"
id="path15740"
inkscape:path-effect="#path-effect15742"
inkscape:original-d="m 33.072916,22.489583 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,23.8125 c 0.441238,0 0.882208,0 1.322917,0"
id="path15744"
inkscape:path-effect="#path-effect15746"
inkscape:original-d="m 33.072916,23.8125 c 0.441238,2.64e-4 0.882208,2.64e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
d="m 33.072916,25.135416 c 0.441238,0 0.882208,0 1.322917,0"
id="path15748"
inkscape:path-effect="#path-effect15750"
inkscape:original-d="m 33.072916,25.135416 c 0.441238,2.65e-4 0.882208,2.65e-4 1.322917,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 38.364583,17.197916 c 0.441033,2.205167 0.882006,4.410027 0.66146,6.173808 -0.220546,1.76378 -1.102473,3.086671 -1.763885,2.645798 -0.661412,-0.440872 -1.102375,-2.645689 -0.881937,-4.630181 0.220438,-1.984493 1.102365,-3.748347 1.984362,-5.512341"
id="path15752"
inkscape:path-effect="#path-effect15754"
inkscape:original-d="m 38.364583,17.197916 c 0.441238,2.205126 0.882208,4.409987 1.322917,6.614584 -0.881698,1.323207 -1.763626,2.646098 -2.645834,3.96875 -0.440716,-2.204641 -0.881679,-4.409458 -1.322916,-6.614584 0.882226,-1.763659 1.764154,-3.527513 2.645833,-5.291666" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="29.104166"
y="29.104166"
id="text15784"><tspan
sodipodi:role="line"
id="tspan15782"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="29.104166">Event</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="29.104166"
y="33.072914"
id="tspan15786">Queue</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2)"
d="m 48.947917,27.78125 c -3.527778,3.527777 -7.055555,7.055555 -10.583334,10.583333"
id="path15788"
sodipodi:nodetypes="cc"
inkscape:original-d="m 48.947917,27.78125 c -3.527513,3.528042 -7.055291,7.05582 -10.583334,10.583333"
inkscape:path-effect="#path-effect15790" />
<text
xml:space="preserve"
style="font-size:3.175px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
x="39.6875"
y="41.010418"
id="text17406"><tspan
sodipodi:role="line"
id="tspan17404"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="41.010418">Override</tspan><tspan
sodipodi:role="line"
style="stroke-width:0.2;stroke:none;fill:#000000;fill-opacity:1"
x="39.6875"
y="44.979168"
id="tspan17408">Action Performed</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

297
pics/jd-04-16-01.svg Normal file
View File

@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
sodipodi:docname="jd-04-16-01.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.906334"
inkscape:cx="240.12283"
inkscape:cy="78.206318"
inkscape:window-width="1546"
inkscape:window-height="921"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid132" />
</sodipodi:namedview>
<defs
id="defs2">
<inkscape:path-effect
effect="bspline"
id="path-effect1100"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1096"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1092"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1088"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1084"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1080"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1076"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1072"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1068"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="32.270699"
id="text188"><tspan
sodipodi:role="line"
id="tspan186"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="32.270699">Thread-3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="19.041531"
id="text188-3"><tspan
sodipodi:role="line"
id="tspan186-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="19.041531">main</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="52.688068"
y="19.041531"
id="text188-3-1"><tspan
sodipodi:role="line"
id="tspan186-2-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="52.688068"
y="19.041531">now</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="12.426948"
id="text188-69"><tspan
sodipodi:role="line"
id="tspan186-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="12.426948">Thread-0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="5.8123641"
id="text188-1"><tspan
sodipodi:role="line"
id="tspan186-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="5.8123641">Thread-1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="25.656115"
id="text188-6"><tspan
sodipodi:role="line"
id="tspan186-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="25.656115">Thread-2</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 19.84375,19.84375 c 20.725958,0 41.451654,0 62.177082,0"
id="path1066"
inkscape:path-effect="#path-effect1068"
inkscape:original-d="m 19.84375,19.84375 c 20.725958,2.64e-4 41.451654,2.64e-4 62.177082,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 50.270833,13.229167 c 7.496791,0 14.993321,0 22.489583,0"
id="path1070"
inkscape:path-effect="#path-effect1072"
inkscape:original-d="m 50.270833,13.229167 c 7.496791,2.64e-4 14.993321,2.64e-4 22.489583,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 48.947916,6.6145832 c 9.260681,0 18.521098,0 27.78125,0"
id="path1074"
inkscape:path-effect="#path-effect1076"
inkscape:original-d="m 48.947916,6.6145832 c 9.260681,2.646e-4 18.521098,2.646e-4 27.78125,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 54.239583,26.458333 c 7.496791,0 14.99332,0 22.489583,0"
id="path1078"
inkscape:path-effect="#path-effect1080"
inkscape:original-d="m 54.239583,26.458333 c 7.496791,2.65e-4 14.99332,2.65e-4 22.489583,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 51.593749,31.75 c 3.087071,0 6.173875,0 9.260417,0"
id="path1082"
inkscape:path-effect="#path-effect1084"
inkscape:original-d="m 51.593749,31.75 c 3.087071,2.64e-4 6.173875,2.64e-4 9.260417,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="M 47.624999,19.84375 C 48.065948,15.434263 48.50692,11.024541 48.947916,6.6145832"
id="path1086"
inkscape:path-effect="#path-effect1088"
inkscape:original-d="m 47.624999,19.84375 c 0.441238,-4.409458 0.882209,-8.81918 1.322917,-13.2291668" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 48.947916,19.84375 c 0.440932,-2.204658 0.881904,-4.409519 1.322917,-6.614583"
id="path1090"
inkscape:path-effect="#path-effect1092"
inkscape:original-d="m 48.947916,19.84375 c 0.441238,-2.204597 0.882208,-4.409458 1.322917,-6.614583" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 50.270833,19.84375 c 0.441004,3.96904 0.881976,7.93779 1.322916,11.90625"
id="path1094"
inkscape:path-effect="#path-effect1096"
inkscape:original-d="m 50.270833,19.84375 c 0.441237,3.969014 0.882208,7.937764 1.322916,11.90625" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 51.593749,19.84375 c 0.882072,2.205179 1.764017,4.410042 2.645834,6.614583"
id="path1098"
inkscape:path-effect="#path-effect1100"
inkscape:original-d="m 51.593749,19.84375 c 0.882208,2.205125 1.764155,4.409987 2.645834,6.614583" />
<circle
id="path1102"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="47.625"
cy="19.84375"
r="0.43695933" />
<circle
id="path1104"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="48.947918"
cy="19.84375"
r="0.43695933" />
<circle
id="path1106"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="50.270832"
cy="19.84375"
r="0.43695933" />
<circle
id="path1108"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="51.59375"
cy="19.84375"
r="0.43695933" />
<circle
id="path1110"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="52.916664"
cy="19.84375"
r="0.43695933" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

217
pics/jd-04-16-02.svg Normal file
View File

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
sodipodi:docname="jd-04-16-02.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="4.5784497"
inkscape:cx="271.48928"
inkscape:cy="94.46429"
inkscape:window-width="1546"
inkscape:window-height="921"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid132"
visible="false" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="TriangleStart"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="TriangleStart"
markerWidth="5.3244081"
markerHeight="6.155385"
viewBox="0 0 5.3244081 6.1553851"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect1100"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1096"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1092"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1088"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1084"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1080"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1076"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1072"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1068"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="34.171467"
y="26.407534"
id="text188-3-1"><tspan
sodipodi:role="line"
id="tspan186-2-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="34.171467"
y="26.407534">10000x10000</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
id="rect1274"
width="31.75"
height="26.458334"
x="31.75"
y="10.583333" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
id="rect1298"
width="15.875"
height="13.229166"
x="75.40625"
y="9.260417" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
id="rect1298-7"
width="15.875"
height="13.229166"
x="75.40625"
y="25.135416" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
id="rect1298-1"
width="15.875"
height="13.229166"
x="93.927078"
y="9.260417" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
id="rect1298-9"
width="15.875"
height="13.229166"
x="93.927078"
y="25.135416" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#TriangleStart)"
d="m 65.112471,24.239808 h 7.9375"
id="path1338" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

409
pics/jd-04-16-04.svg Normal file
View File

@ -0,0 +1,409 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
sodipodi:docname="jd-04-16-04.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.906334"
inkscape:cx="240.12284"
inkscape:cy="78.206318"
inkscape:window-width="1546"
inkscape:window-height="921"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid132" />
</sodipodi:namedview>
<defs
id="defs2">
<inkscape:path-effect
effect="bspline"
id="path-effect751"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect747"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect743"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect739"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1100"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1096"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1092"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1088"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1084"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1080"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1076"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1072"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1068"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="32.270699"
id="text188"><tspan
sodipodi:role="line"
id="tspan186"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="32.270699">Thread-3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="19.041531"
id="text188-3"><tspan
sodipodi:role="line"
id="tspan186-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="19.041531">main</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="53.845882"
y="19.041531"
id="text188-3-1"><tspan
sodipodi:role="line"
id="tspan186-2-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="53.845882"
y="19.041531">join</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="80.283051"
y="19.433115"
id="text188-3-1-3"><tspan
sodipodi:role="line"
id="tspan186-2-0-8"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="80.283051"
y="19.433115">now</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="12.426948"
id="text188-69"><tspan
sodipodi:role="line"
id="tspan186-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="12.426948">Thread-0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="5.8123641"
id="text188-1"><tspan
sodipodi:role="line"
id="tspan186-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="5.8123641">Thread-1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="22.260984"
y="25.656115"
id="text188-6"><tspan
sodipodi:role="line"
id="tspan186-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="22.260984"
y="25.656115">Thread-2</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 19.84375,19.84375 c 20.725958,0 41.451654,0 62.177082,0"
id="path1066"
inkscape:path-effect="#path-effect1068"
inkscape:original-d="m 19.84375,19.84375 c 20.725958,2.64e-4 41.451654,2.64e-4 62.177082,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 50.270833,13.229167 c 7.496791,0 14.993321,0 22.489583,0"
id="path1070"
inkscape:path-effect="#path-effect1072"
inkscape:original-d="m 50.270833,13.229167 c 7.496791,2.64e-4 14.993321,2.64e-4 22.489583,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 48.947916,6.6145832 c 9.260681,0 18.521098,0 27.78125,0"
id="path1074"
inkscape:path-effect="#path-effect1076"
inkscape:original-d="m 48.947916,6.6145832 c 9.260681,2.646e-4 18.521098,2.646e-4 27.78125,0" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 52.916667,26.458333 c 4.409986,0 8.819709,0 13.229166,0"
id="path1078"
inkscape:path-effect="#path-effect1080"
inkscape:original-d="m 52.916667,26.458333 c 4.409986,2.65e-4 8.819709,2.65e-4 13.229166,0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 51.593749,31.75 c 5.291933,0 10.583597,0 15.875001,0"
id="path1082"
inkscape:path-effect="#path-effect1084"
inkscape:original-d="m 51.593749,31.75 c 5.291933,2.65e-4 10.583597,2.65e-4 15.875001,0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="M 47.624999,19.84375 C 48.065948,15.434263 48.50692,11.024541 48.947916,6.6145832"
id="path1086"
inkscape:path-effect="#path-effect1088"
inkscape:original-d="m 47.624999,19.84375 c 0.441238,-4.409458 0.882209,-8.81918 1.322917,-13.2291668" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 48.947916,19.84375 c 0.440932,-2.204658 0.881904,-4.409519 1.322917,-6.614583"
id="path1090"
inkscape:path-effect="#path-effect1092"
inkscape:original-d="m 48.947916,19.84375 c 0.441238,-2.204597 0.882208,-4.409458 1.322917,-6.614583" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 50.270833,19.84375 c 0.441004,3.96904 0.881976,7.93779 1.322916,11.90625"
id="path1094"
inkscape:path-effect="#path-effect1096"
inkscape:original-d="m 50.270833,19.84375 c 0.441237,3.969014 0.882208,7.937764 1.322916,11.90625" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;stroke-opacity:1"
d="m 51.593749,19.84375 c 0.441033,2.205165 0.882006,4.410028 1.322918,6.614583"
id="path1098"
inkscape:path-effect="#path-effect1100"
inkscape:original-d="m 51.593749,19.84375 c 0.441237,2.205125 0.88221,4.409988 1.322918,6.614583"
sodipodi:nodetypes="cc" />
<circle
id="path1102"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="47.625"
cy="19.84375"
r="0.43695933" />
<circle
id="path1104"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="48.947918"
cy="19.84375"
r="0.43695933" />
<circle
id="path1106"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="50.270832"
cy="19.84375"
r="0.43695933" />
<circle
id="path1108"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="51.59375"
cy="19.84375"
r="0.43695933" />
<circle
id="path1110"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="52.916664"
cy="19.84375"
r="0.43695933" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round"
d="m 66.145832,26.458333 c 0.440932,-2.204657 0.881904,-4.409519 1.322917,-6.614583"
id="path737"
inkscape:path-effect="#path-effect739"
inkscape:original-d="m 66.145832,26.458333 c 0.441238,-2.204596 0.882209,-4.409458 1.322917,-6.614583" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round"
d="m 67.468749,31.75 c 0.440947,-3.968518 0.881919,-7.937268 1.322917,-11.90625"
id="path741"
inkscape:path-effect="#path-effect743"
inkscape:original-d="m 67.468749,31.75 c 0.441238,-3.968486 0.882208,-7.937236 1.322917,-11.90625" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round"
d="m 72.760416,13.229167 c 0.441338,2.20669 0.882005,4.410027 1.322916,6.614583"
id="path745"
inkscape:path-effect="#path-effect747"
inkscape:original-d="m 72.760416,13.229167 c 0.446349,2.205688 0.882208,4.409986 1.322916,6.614583" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round"
d="m 76.729166,6.6145832 c 0.441001,4.4100108 0.881973,8.8197328 1.322916,13.2291668"
id="path749"
inkscape:path-effect="#path-effect751"
inkscape:original-d="M 76.729166,6.6145832 C 77.170403,11.02457 77.611374,15.434292 78.052082,19.84375" />
<circle
id="path753"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="67.46875"
cy="19.84375"
r="0.43695933" />
<circle
id="path755"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="68.791664"
cy="19.84375"
r="0.43695933" />
<circle
id="path757"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="74.083336"
cy="19.84375"
r="0.43695933" />
<circle
id="path759"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="78.052086"
cy="19.84375"
r="0.43695933" />
<circle
id="path761"
style="fill:#000000;stroke:none;stroke-width:0.264583"
cx="79.375"
cy="19.84375"
r="0.43695933" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

374
pics/jd-04-18-01.svg Normal file
View File

@ -0,0 +1,374 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
sodipodi:docname="jd-04-18-01.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="3.0476943"
inkscape:cx="240.8378"
inkscape:cy="91.052438"
inkscape:window-width="1546"
inkscape:window-height="921"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid132"
visible="false" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="TriangleStart"
refX="0"
refY="0"
orient="auto-start-reverse"
inkscape:stockid="TriangleStart"
markerWidth="5.3244081"
markerHeight="6.155385"
viewBox="0 0 5.3244081 6.1553851"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5.77,0 -2.88,5 V -5 Z"
id="path135" />
</marker>
<marker
style="overflow:visible"
id="Dot"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Dot"
markerWidth="5.6666665"
markerHeight="5.6666665"
viewBox="0 0 5.6666667 5.6666667"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.5)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 5,0 C 5,2.76 2.76,5 0,5 -2.76,5 -5,2.76 -5,0 c 0,-2.76 2.3,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="Dot1"
sodipodi:nodetypes="sssss" />
</marker>
<inkscape:path-effect
effect="bspline"
id="path-effect748"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect751"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect747"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect743"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect739"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1100"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1096"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1092"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1088"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1084"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1080"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1076"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1072"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect1068"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect748-9"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect748-9-3"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect748-9-4"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="21.060833"
y="24.29933"
id="text188-1"><tspan
sodipodi:role="line"
id="tspan186-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="21.060833"
y="24.29933">Thread-1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="81.915001"
y="24.29933"
id="text188-1-1"><tspan
sodipodi:role="line"
id="tspan186-3-7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="81.915001"
y="24.29933">Thread-2</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;font-family:'GOST type A';-inkscape-font-specification:'GOST type A, Normal';fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round"
x="43.65625"
y="11.90625"
id="text638"><tspan
sodipodi:role="line"
id="tspan636"
style="stroke-width:0.291306"
x="43.65625"
y="11.90625" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="42.2402"
y="4.3285809"
id="text188-1-8"><tspan
sodipodi:role="line"
id="tspan186-3-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="42.2402"
y="4.3285809">Ожидает ответа</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="42.2402"
y="9.6202421"
id="tspan651">для продолжения</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="42.2402"
y="14.911904"
id="tspan653">работы</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306;stroke-linecap:round"
x="42.2402"
y="34.755665"
id="text188-1-8-8"><tspan
sodipodi:role="line"
id="tspan186-3-1-7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="42.2402"
y="34.755665">Ожидает ответа</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="42.2402"
y="40.047325"
id="tspan651-6">для продолжения</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'IBM Plex Mono';-inkscape-font-specification:'IBM Plex Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.291306"
x="42.2402"
y="45.338989"
id="tspan653-0">работы</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;marker-start:url(#Dot);marker-end:url(#TriangleStart)"
d="m 29.104166,19.84375 c 0,-4.409458 0,-8.81918 1.984547,-11.0241734 1.984547,-2.2049934 5.953218,-2.2049934 9.921703,-2.2049934"
id="path746"
inkscape:path-effect="#path-effect748"
inkscape:original-d="m 29.104166,19.84375 c 2.65e-4,-4.409458 2.65e-4,-8.81918 0,-13.2291668 3.969094,2.646e-4 7.937765,2.646e-4 11.90625,0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;marker-start:url(#TriangleStart)"
d="m 93.927083,19.84375 c 0,-4.409458 0,-8.81918 -1.984547,-11.0241733 C 89.957989,6.6145833 85.989318,6.6145833 82.020833,6.6145833"
id="path746-5"
inkscape:path-effect="#path-effect748-9"
inkscape:original-d="m 93.927083,19.84375 c -2.65e-4,-4.409458 -2.65e-4,-8.81918 0,-13.2291667 -3.969094,2.65e-4 -7.937765,2.65e-4 -11.90625,0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;marker-end:url(#TriangleStart);marker-start:url(#Dot)"
d="m 93.927083,26.458333 c 0,4.409458 0,8.81918 -1.984547,11.024173 C 89.957989,39.6875 85.989318,39.6875 82.020833,39.6875"
id="path746-5-6"
inkscape:path-effect="#path-effect748-9-3"
inkscape:original-d="m 93.927083,26.458333 c -2.65e-4,4.409458 -2.65e-4,8.81918 0,13.229167 -3.969094,-2.65e-4 -7.937765,-2.65e-4 -11.90625,0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.291306;stroke-linecap:round;marker-start:url(#TriangleStart);marker-end:"
d="m 29.104167,26.458333 c 0,4.409458 0,8.81918 1.984547,11.024173 1.984547,2.204994 5.953218,2.204994 9.921703,2.204994"
id="path746-5-0"
inkscape:path-effect="#path-effect748-9-4"
inkscape:original-d="m 29.104167,26.458333 c 2.65e-4,4.409458 2.65e-4,8.81918 0,13.229167 3.969094,-2.65e-4 7.937765,-2.65e-4 11.90625,0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -80,7 +80,7 @@
06-10 & Менеджер размещения или компоновщик, как следует из названия -- это специальный объект, который помещается на некоторые компоненты и осуществляет автоматическую расстановку добавляемых к нему, компоненту, согласно каким-то прописанным внутри данного менеджера размещения правилам. Чуть забегая вперёд можно сказать, что это контейнеры, реализующие программный интерфейс под названием RootPaneContainer, но это так, шестерёнки, которые заучивать не нужно. Важно понимать, что именно делают компоновщики -- размещают компоненты. Список менеджеров можно увидеть на слайде. Все их можно создать, так сказать пощупать, попробовать в работе и это достаточно сильно зависит от задачи -- какой именно выбрать для использования на том или ином экране. О трёх самых популярных сейчас поговорим.\\ \hline
06-11 & По умолчанию в библиотеке swing используется компоновщик borderLayout. Он располагает всё, что вы ему передаёте в центре, но также у него есть ещё четыре положения. маленькие области по краям. Если мы не занимаем какую-то область компонентом, она схломывается до нулевых размеров, оставляя место другим компонентам. Поэтому, если мы хотим какой-то наш компонент расположить где-то не в центре, мы должны это явно указать при добавлении. Тут немного неочевидно, поэтому запомните, пожалуйста, что при добавлении надо указать ещё один параметр, константу, например, BorderLayout.SOUTH. вот так неожиданно сюда сторона света приплелась, юг. Или вот, например, FlowLayout() будет располагать элементы друг за другом в том порядке, как мы пишем, слева направо, сверху вниз, как мы привыкли писать рукой на бумаге. Компоновщик-сетка при создании принимает на вход число строк и столбцов и располагает компоненты в получившейся сетке. Ну и так далее всякие ГридБэгЛэйауты и прочие. Ещё раз, основная идея, которую надо понять, это не названия компоновщиков, а то, что в свинге вся работа происходит через компоновщики - вот эти вот самые лэйауты, которые как-то по-своему располагают элементы в окошке. Кстати, если это ещё не стало очевидным, кнопки расположились одна поверх другой потому что по умолчанию к фрейму применён БодэЛэйаут, который всё располагает по-умолчанию в центре. \\ \hline
06-11 & По умолчанию в библиотеке swing используется компоновщик borderLayout. Он располагает всё, что вы ему передаёте в центре, но также у него есть ещё четыре положения. маленькие области по краям. Если мы не занимаем какую-то область компонентом, она схломывается до нулевых размеров, оставляя место другим компонентам. Поэтому, если мы хотим какой-то наш компонент расположить где-то не в центре, мы должны это явно указать при добавлении. Тут немного неочевидно, поэтому запомните, пожалуйста, что при добавлении надо указать ещё один параметр, константу, например, BorderLayout.SOUTH. вот так неожиданно сюда сторона света приплелась, юг. Или вот, например, FlowLayout() будет располагать элементы друг за другом в том порядке, как мы пишем, слева направо, сверху вниз, как мы привыкли писать рукой на бумаге. Компоновщик-сетка при создании принимает на вход число строк и столбцов и располагает компоненты в получившейся сетке. Ну и так далее всякие ГридБэгЛэйауты и прочие. Ещё раз, основная идея, которую надо понять, это не названия компоновщиков, а то, что в свинге вся работа происходит через компоновщики - вот эти вот самые лэйауты, которые как-то по-своему располагают элементы в окошке. Кстати, если это ещё не стало очевидным, кнопки расположились одна поверх другой потому что по умолчанию к фрейму применён БодэЛэйаут, который всё располагает по умолчанию в центре. \\ \hline
06-12 & Одними лэйаутами сыт не будешь, так что разработчики джавы придумали использовать не только компоненты сами по себе, но ещё и группы элементов, группы элементов складывают на так называемые JPanel. И внутри каждой панели мы можем использовать свой лэйаут. Jpanel - это по умолчанию невидимый прямоугольник, основным свойством которого для нас в данный момент является то, что на нём может находиться собственный компоновщик, что открывает для нас близкие к бесконечным возможности по расстановке компонентов на экране. Например, мы можем создать для нашего окна некую панель с кнопками внизу (или вверху, не важно), а всё остальное пространство оставить под другие важные вещи. На слайде можно увидеть код панели, добавление её в нижнюю часть основного экрана, расположение внутри панели компоновщика и двух кнопок. И, соответственно, результат. Обратите особенное внимание на 25ю строку, на экран мы добавляем не кнопки по отдельности, а компонент, на который предварительно добавили кнопки. \\ \hline
@ -207,4 +207,3 @@
\end{longtable}
\end{document}

View File

@ -217,9 +217,7 @@
То есть, если написать тот же самый метод принтИнфо, с параметром коробка и обобщённым аргументом не экстендс, а супер животное, то этот код также не будет работать, потому что метод будет ожидать не животное и наследников, а животное и родителей, то есть обжект. \\ \hline
08-29 &
Обобщённые классы или интерфейсы связаны не только из-за связи между их типами. Однако можно использовать подстановочные символы (wildcards) для создания связи между обобщёнными классами и интерфейсами. С обычными необобщёнными классами Кота и Животного имеет смысл писать какой-нибудь простой незатейливый код, который мы уже десятки, если не сотни раз видели и писали сами. Этот код показывает, что наследование работает по правилу подчинённых типов: класс Cat является подклассом класса Animal, и расширяет его.
08-29 & Обобщённые классы или интерфейсы связаны не только из-за связи между их типами. Однако можно использовать подстановочные символы (wildcards) для создания связи между обобщёнными классами и интерфейсами. С обычными необобщёнными классами Кота и Животного имеет смысл писать какой-нибудь простой незатейливый код, который мы уже десятки, если не сотни раз видели и писали сами. Этот код показывает, что наследование работает по правилу подчинённых типов: класс Cat является подклассом класса Animal, и расширяет его.
% 02
И правило не работает для обобщённых типов. Но, если Cat является дочерним типом для Animal, то какая связь между Коробкой с <Cat> и Коробкой с <Animal>? Несмотря на то, что Cat является подтипом Animal, Box<Cat> не является подтипом Box<Animal>. Это разные типы. Общим предком для Box<Animal> и Box<Cat> является Box с подстановочным символом <?>.

View File

@ -1,25 +1,465 @@
\documentclass[../j-spec.tex]{subfiles}
\begin{document}
\section{Обобщения}
\section{Многопоточность}
\begin{longtable}{|p{35mm}|p{135mm}|}
\hline
Экран & Слова \\ \hline
\endhead
Титул & Здравствуйте, добро пожаловать на курс посвящённый инструментарию разработчика на джава \\ \hline
Титул & Здравствуйте, рад всех приветствовать на курсе посвящённом инструментарию разработчика на джава \\ \hline
Отбивка & в сегодняшней лекции коснёмся одного из ключевых понятий программирования на языке джава -- обобщённого программирования. \\ \hline
Отбивка & в сегодняшней лекции будем говорить о чрезвычайно важном понятии для всего современного программирования -- многопоточности. \\ \hline
На прошлом уроке & На прошлом уроке поговорили о крутом полиморфизме. Поговорили про дженерики. Попробовали обойтись без них, потом сделали с ними, сравнили. Совсем чуть-чуть проговорили про дженерик интерфейсы, обобщённые методы и подстановочные символы, а именно, ограничения сверху и снизу, дополнительно снова немного поковырялись в шестерёнках, поверхностно рассказал о том, что такое загрязнение кучи. \\ \hline
На этом уроке & Нас ждут великие дела. Будем говорить о Многопроцессности и многопроцессорности. вполне очевидно, что от неё придём к нашей основной теме -- Многопоточности. Если говорить о многопоточности, нельзя не сказать об асинхронности. Соответственно, обсудим эти прекрасные явления, опираясь на изучаемый нами язык, а значит обязательно рассмотрим Интерфейс Runnable и класс Thread. ExecutorService. В многопоточности, как и в остальных областях программирования не обошлось без проблем и сегодня наконец-то закроем вопрос методов класса Object. об этом действительно нужно сказать отдельно. \\ \hline
09-01 & Многопоточность -- это возможность одновременного выполнения нескольких задач в рамках одного процесса. Она является одним из ключевых понятий в программировании и широко используется в современных приложениях. Многопоточность позволяет улучшить производительность программы, в некоторых случаях даже распределяя нагрузку между несколькими ядрами процессора. В отличие от многопроцессности, где каждый процесс имеет своё отдельное адресное пространство, при многопоточности все потоки работают в рамках одного адресного пространства. Это позволяет им обмениваться информацией и синхронизировать свою работу. Кстати об этом, асинхронность -- это ещё одна важная концепция, которая позволяет выполнять задачи без блокирования основного потока выполнения. Архитектура современных процессоров становится всё более сложной, и теперь они имеют несколько ядер, что позволяет параллельно выполнять несколько задач. Однако, создание реальных параллельных вычислителей остается сложной задачей, и требует больших затрат на исследования и разработку. \\ \hline
09-02 & Вместо введения я хочу вернуть вас на пару-тройку лекций назад, кое что напомнить и постараюсь нагнать немного жути. Но вас же уже не так просто испугать, верно? Расскажу я про уже известные вам вещи с одной целью -- показать, что все вот эти многопоточности -- это не какая-то абстракция, а то, с чем придётся сталкиваться при работе с языком чуть чаще, чем каждый день. Итак, когда Вы создаёте приложение на любой платформе, работающей поверх операционной системы компьютера, у вас создаётся процесс, и как минимум создаётся один поток (мэйн). В джаве таких потоков для каждого процесса создаётся несколько -- Main, Error, GC, EDT, мы их обсуждали когда разговаривали о графических интерфейсах пользователя, эти потоки нужны были для понимания процесса работы с окошком. Поскольку процессоры у нас ещё не очень хорошо работают с параллельной логикой -- создаётся псевдо-параллельность, об этом сейчас тоже поговорим. Если в одном из потоков происходит исключение, другой поток не обязательно об этом узнает. На слайде то мы всё видим, но это всё консоль, среда разработки, понятно, что мы всё видим. А что будет, если мы создадим jar файл, и запустим наше приложение отдельно от среды разработки? Вариантов, если подумать, может быть масса, а вы как думаете? Я думаю тут могут быть два основных варианта - визуально ничего не произойдёт, или приложение полностью грохнется. Второй вариант, очевидно, лучше. Надо ли объяснять почему? (естественно, что-то в приложении произошло, а ни мы ни пользователь не в курсе, очень плохо). На самом деле в джава -- на экране пользователя ничего не произойдёт и это очень плохо. Прониклись проблемой? \\ \hline
09-03 & Идеи многопроцессности начали развиваться задолго до того, как появился первый персональный компьютер. Тут можно уйти в глубокие дебри не только цифровой схемотехники, но и архитектуры компьютеров, потому что люди старались и стараются, придумывают, как улучшить. Сейчас существует чуть больше, чем очень много разных архитектур компьютеров, но основные пока что сводятся к двум видам -- гарвардская архитектура и фон-Нейманова архитектура, а также их разнообразные сочетания. Уже в 50-х годах прошлого века были запущены проекты, направленные на создание компьютерных систем, способных выполнять одновременно несколько процессов. Однако, тогдашняя технология не позволяла реализовать такие системы в полной мере. Со временем технологии стали развиваться, и появилась возможность создавать компьютеры с несколькими процессорами, их ещё называют мультипроцессорные системы. Многопроцессорность позволяет увеличить производительность компьютера, распределяя нагрузку на несколько ядер процессора. Более современные компьютеры часто имеют более одного процессора на материнской плате. Использование многопроцессорности позволяет ускорить выполнение задач, требующих больших вычислительных мощностей, таких как научные расчеты, 3D-моделирование и игры. В мультипроцессорных системах можно использовать разные типы процессоров, которые могут выполнять различные задачи, что увеличивает гибкость и эффективность работы системы в целом. Однако, часто, написание программ для мультипроцессорных систем может быть сложнее, чем для однопроцессорной системы, из-за необходимости управления распределением нагрузки между процессорами. Кроме того, существуют и разные подходы к реализации многопроцессорности, такие как симметричная многопроцессорность, где все процессоры равноправны, и асимметричная многопроцессорность, где один процессор является основным, а остальные используются для выполнения второстепенных задач. Но нам это не особенно интересно, на самом деле за нас балансировку выполняет виртуальная машина джава. А многопроцессность, о которой мы начали говорить -- это возможность компьютером выполнять одновременно несколько процессов. \\ \hline
09-04 & То есть, основная идея многопроцессности -- удобство пользователя -- проще всего реализуется через мультипроцессорность. Но до мультипроцессорности технологии дошли сравнительно недавно, как же инженеры выкручивались? Надо было как-то одновременно и показывать окно какого-нибудь редактора и обрабатывать запросы в сеть, проверять электронную почту и получать уведомления от подсистем операционной системы. Выкрутились. Придумали псевдо-параллельность, то есть технологию, при которой какое-то количество времени исполняется один процесс, какое-то время другой, потом снова первый и третий и другие и так далее. А поскольку процессоры сейчас с бешеными гигагерцами, но уже тогда были в сотню кило или даже мегагерц, то для человека создаётся впечатление, что это происходит параллельно, напомню, человеческий глаз очень плохо различает картинки, меняющиеся быстрее, чем с частотой 60 герц, а тут мегагерцы. Технология называется HyperThreading. Потом уже, когда придумали, как вкрутить в один компьютер несколько действительно параллельных процессоров, сделали реальную многопроцессность с действительно параллельными вычислениями. \\ \hline
09-05 & Ну а дальше, поняли насколько это всё классно и уже не смогли остановиться, поняв, что многопроцессность -- это удобно, но гораздо удобнее, если ещё и приложения смогут выполнять какие-нибудь вычисления в несколько потоков. Так и назвали -- многопоточность. Не путайте с потоками ввода-вывода, то были потоки в смысле Stream, тут потоки в смысле Thread, то есть буквально, нити выполнения. Внезапно повеяло теорией мультивселенных, не обращайте внимания, так всегда происходит. Многопоточность -- это способность одного приложения выполнять одновременно несколько действий. Реальная это многопоточность или только так кажется -- нам сейчас не особенно важно, нам важно, что это уже существует, мы неоднократно видели это в работе и важно понимание механизма. То есть, ещё раз. Многопоточность -- это параллельность в рамках одного процесса, многопроцессность -- это параллельность в глазах человека, многопроцессорность -- это архитектурная особенность, благодаря которой облегчается реализация задач многопроцессности и многопоточности. Единственное, что пока не обговорили -- это асинхронность. не переживайте, я помню, обязательно обсудим.
%02
Давайте резюмируем, пока мы смотрим на эту, хорошо объясняющую картинку. Как я уже неоднократно, по-разному формулируя, говорил, поток -- это одна нить выполнения. Это некая абстракция. Все операционные системы делятся на однозадачные и многозадачные. Раньше такие были, может кто-то помнит, MS-DOS например или какие-нибудь старые операционки для встраиваемых систем. Соответственно, на картинке хорошо видно, как реализуется многозадачность на одном и двух ядрах, для многозадачных систем. Это псевдо-многозадачность, мощные процессоры очень-очень быстро переключаются между задачами, и для человеческого глаза кажется, что всё параллельно. Или, допустим, у нас есть задача какая-то большая, и если мы можем эту задачу выполнять как-то разбив на подзадачи, а потом собрав в одну кучу обратно, получить прирост производительности, то это делается, отдавая задачу двум потокам одновременно. А когда завершается процесс? Когда завершается последний поток. Остаётся только вопрос, как это синхронизировать, когда подзадачи выполнились. И вот снова синхронность/асинхронность, привыкайте-привыкайте. \\ \hline
09-06 & Может показаться, что это вступление затянулось, но я хочу, чтобы это очень прочно закрепилось -- многопоточность повсюду, и так вышло, что мы уже коснулись разработки графических интерфейсов. А значит на примере графических интерфейсов это будет объяснять проще. Каждое окно это отдельный процесс, внутри которого крутится бесконечный цикл, который опрашивает так называемую очередь сообщений. Что это такое? Вот например можно водить по окошку мышкой, и этому окну в очередь прилетает куча много ActionEvent, о том, что на окне мышка шевельнулась, и координаты указателя. А этот бесконечный цикл когда-то, когда ему удобно, опрашивает эту очередь, и как-то обрабатывает события. Вот во фреймворке Swing этот поток называется EDT и его обработчики мы описываем, когда хотим как-то по-своему реагировать на события.
% 02
То есть, например, мы кликнули по окошку и у нас появился курсор для ввода текста. А на самом деле довольно много всего произошло:
- я кликнул мышку (окно об этом ещё вообще ничего не знает)
- внутри мышки замкнулась куча цепей, контроллер внутри мышки отправил сигнал в провод
% 03
- операционная система увидела что пришло событие от аппаратной части (мышка, координаты на рабочем столе)
- ОС смотрит, О, у меня ж по этим координатам сейчас окошко наверху висит
% 04
- ОС посылает окошку сообщение: эй, окошко, по тебе тут кликнули, вот тут
- Окошко крутит себе свой бесконечный цикл, который читает такие сообщения
% 05
- Окошко читает сообщение от ОС и думает О, по мне ткнули мышкой вот тут по локальным координатам окна
- Окошко смотрит, а в какой именно мой компонент ткнули
% 06
- Окошко смотрит, есть-ли соответствующий обработчик этого события
- Окошко отдаёт это событие обработчику компонента
% 07
- Обработчик компонента зажигает по данным координатам курсор.
Из этой длинной истории надо понять одну вещь - даже если я совершил очень долгое последовательное действие, оно выполняется с какими-то другими действиями параллельно и обработка событий - это отдельный поток, у которого есть очередь сообщений. \\ \hline
09-07 & Давайте делать на основе вышесказанного гадости, исключительно, ради подтверждения. Вот, предположим, у нас есть какое-то окошко с кучей компонентов, возможно даже с клиентом чата. Здесь я предлагаю вам открыть среду разработки, особенно если вы смотрите это в записи. Мы можем по окошку кликать, двигать ползунки, и всё такое, это всё как-то обрабатывается и делается вид, что оно живое. Положим, повесим обработчик нажатия на нашу кнопку Send.
% 02
и зациклим его бесконечным циклом, то есть будет очевидно, что работа окна - это один поток, который мы убили так, что восстановить его уже будет нереально. То есть оно не залагало, а прям больше вообще не реагирует на внешние раздражители. События то прилетают, а вот обработчик занят циклом. Можно видеть, что даже отрисовка отжатия кнопки обратно не произошла и окно не реагирует в том числе на нажатие крестика. Это какая-то прям крайняя ситуация. Можно облегчить, вместо бесконечного цикла создать какой-то просто очень долгий.
% 03
Пока обработчик висит в цикле, что-то подвигаем и нажмём и увидим, что поток значительно загрузился, но потом его всё-таки отпустило и всё, что мы нажимали -- прилетело в окно и обработалось им, кстати, это хорошо показывает полезность множественных кликов по кнопкам в надежде, что что-то перестанет зависать. Так вот именно такие задачи, которые надолго вешают наши потоки -- принято отдавать другим потокам, называемым фоновыми. Вся сложность будет выполняться в нём, а основной поток, например, с графическим интерфейсом мы отпускаем, чтобы он дальше обрабатывал очередь событий. Мы должны создать то, что называется асинхронность. Ведь правда, пользователю не хочется ждать пока мы что-то посчитаем, а хочется сразу что-то ещё подвигать на окошке. То есть потоки создаются для распределения больших задач, или для отвода глаз, то есть создания фоновых задач. \\ \hline
09-08 & Пользоваться потоками это классно и удобно, но хорошо бы уметь создавать свои собственные. Одним из самых популярных способов создания собственных потоков в языке джава является использование класса Thread. \\ \hline
09-09 & Создание потока может производиться двумя способами, самый популярный -- создание собственного класса -- наследника класса Thread. Здесь всё просто -- создаём свой класс, например, MyThread, наследник класса Thread. Но внезапно этого оказывается недостаточно, то есть выполнение действия внутри класса-наследника тред -- не гарантирует того, что действие будет выполнено в отдельном потоке, на слайде видно, что в результате вызова некоторых, пока что магических, методов, мы получаем идентичные названия потоков для главного метода и метода в классе-наследнике.
% 02
Любое учебное пособие утверждает, что для того, чтобы использовать класс-наследник треда, нужно переопределить метод ран, и если так сделать, то всё написанное в это методе будет выполнено в отдельном потоке. проверим это утверждение на практике и увидим, что учебники немного лукавят, мы переопределили метод run, вызвали из него метод с так называемой бизнес-логикой, то есть с действиями, которые предполагаем в другом потоке, а работает всё равно вся программа в одном и том же потоке. Выходит, что никакой многопоточности не существует? можно подумать так, но среда разработки начинает нам подсказывать, что с методом ран что-то не совсем так.
% 03
Если приглядеться к сообщению среды разработки, можно будет увидеть, что она предполагает, что мы ошибочно вызвали ран вместо старт. И правда, если внимательно почитать документацию, то там написано -- метод старт разделяет выполнение программы на два потока -- основной, в котором вызывался собственно метод старт, и второй, в котором будет выполнена логика, описанная в переопределённом методе ран. На снимке экрана видно, что первое сообщение прилетело от потока с названием мейн, а второе от потока с названием Тред-0 \\ \hline
09-10 & Итак, как правильно создавать поток с помощью наследования? Переопределяем метод run(){} и всё, что мы в нём напишем, будет выполнено в отдельном потоке. Но при условии, что мы верно запустим созданный поток. Из только что приведённый примеров мы узнали, что у класса Тред есть и статические методы. например, статический метод каррентТред, который является репрезентацией текущего потока, выражаясь проще -- это объект текущего потока, мы можем им как-то управлять, мы можем узнать его имя и совершить какие-то ещё интересные действия, вроде создания демона или изменения приоритета, которые совершаются соответствующими методами. Во вновь созданном потоке сделали единственную вещь -- вывели в консоль его имя. Далее создали экземпляр потока, дополнительно обратите внимание, что просто создание объекта потока ничего не даёт, он не запущен и не работает, и далее в мэйне мы его запустили вызвав метод старт.
% 02
Это самый простой способ запустить второй третий и вообще любое количество одинаковых потоков. У объектов класса тред есть методы .setName(); setPriority() и так далее, но это уже мелочи, посмотреть на список методов можно самостоятельно. Например у класса Thread есть конструктор, который принимает на вход строку, которая будет означать имя создаваемого конструктором потока. А для того, чтобы им воспользоваться нам надо сделать что? правильно, создать в потоке-наследнике конструктор, и вызвать супер(имя)). Вообще, можно довольно много интересного сделать с потоками при помощи этих методов и конструкторов, например сразу сделать чтобы создаваемый поток стартовал сам себя, для этого в конструкторе пишем вызов метода start(). И соответственно зачем сохранять идентификатор объекта, если мы можем стартовать поток анонимно. Но это уже такие вещи, больше просто объектно-ориентированные, нежели касающиеся непосредственно потоков. \\ \hline
09-11 & Что можно сделать с потоком? давайте предположим, что у нас есть какая-то долгая задача, чтобы это имитировать, в наследнике можем длинный цикл написать, либо там что-то вообще сильно навсегда зациклено, видим вайл-тру, и допускаем, что поток ждёт каких-то событий, которые могут в него прилететь. Запустив поток видим, что, в общем-то, всё, он зациклен, и остановить мы его никак не можем. Но это ситуация подконтрольная, мы сами её только что в тепличных условиях создали. А что если ситуация возникла без нашего ведома и программа по какой-то причине попала в бесконечный цикл? Верно, со стороны будет казаться что программа зависла и не реагирует на внешние раздражители. Нам надо как-то наш поток остановить снаружи, а как это сделать, как отдать команду потоку на завершение?
% 02
Хорошо посмотрев документацию или исходники класса тред можно обнаружить, что у треда есть метод стоп, но если смотреть очень внимательно, можно увидеть, что он отмечен аннотацией депрекейтед. причём запрещён этот метод аж со второй версии языка, то есть, практически, сразу. Интересно, почему запретили жёстко завершать потоки? давайте порассуждаем. Допустим, что мы отдали потоку какую-то сложную длинную вычислительную задачу и он эту задачу выполняет, помните картинку с псевдо-параллельной многопоточностью? Так вот, выполняет наш поток какую-то задачу, а мы его, получается, прерываем на полу-слове, и неизвестно, что там происходит, может он там ресурсы какие-то захватил, может он скоро, вот-вот, закончил бы. Это всем очевидно? поток там чем-то занимался, возможно, важным, а мы ему топором по голове НА ТЕБЕ и непонятно в каком состоянии данные, что он сделал, что не сделал, и что теперь делать с неживым потоком.
% 03
И придумали потоки завершать мягко. у класса тред внутри есть метод isInterrupted() который возвращает флажок isInterrupted. И вместо while (true) всегда желательно писать while (!isInterrupted()) и где-то снаружи станоится возможным потоку сказать thread.interrupt(); и этот флажок начинает тру возвращать, а следовательно, когда-то цикл прервётся и поток завершит работу. Соответственно задача программиста в том, чтобы сложный метод внутри многопоточной программы так написать, чтобы он иногда проверял этот флажок, и мог мягко завершиться. \\ \hline
09-12 & Это всё довольно скучно и не очень понятно, что с этим делать, потому что слишком абстрактно и теоретически. Раз уж у нас тут неподалёку есть ЧатКлиент, предлагаю на примере чата что нибудь и сделать. Те, кто был на прошлых семинарах, могут припомнить, что мы делали простое окошко управления сервером, где было две кнопки. У ChatServer, как и у любого другого сервера, есть методы старт и стоп, довольно очевидно, чтобы запускать и останавливать сервер, они-то нам и пригодятся. Создадим какой-нибудь хороший класс, и сделаем в нём что-нибудь умное, чтобы все остальные программисты нам завидовали. Отходя немного в сторону от темы, у любого сервера, рано или поздно, появляется задача - ожидать входящего соединения. Не особенно заморачивайтесь что это вообще такое, пока что нам надо понимать только формулировку. И заниматься этим ожиданием соединений будет класс с очень странным названием ServerSocketThread, который будет наследоваться от Thread и мы его пока что будем использовать для тестов. Внутри этого странно названного класса переопределяем метод run(). И описываем в классе конструктор. Из очевидного -- спросим у создающего имя и порт, который нужно будет слушать, и сразу запустим поток. Получится такая вот заготовка.
% 02
Теперь нам очень важно правильно написать работу этого потока. Для начала объяснения пока что в методе run его зациклим. Первый вариант -- это мы его циклим в бесконечность с простым выводом информации о том, что сервер работает. Вот тут будет очень важно понимать что именно делает этот код, поэтому идём обратно в ChatServer и вспоминаем, что мы там написали. Ничего не написали, прямо сейчас -- методы старт и стоп ничего не делает. А если мы предположим, что хотим управлять сервером с графического интерфейса, то когда мы в графическом интерфейсе кликаем по кнопке старт или стоп у нас должны вызываться методы старт и стоп в классе чатСервера. А сам класс ЧатСервер - это у нас пока-что такая картонная дурилка, которая только логирует. Соответственно нам по кнопке Старт надо запустить поток ServerSocketThread а по кнопке Стоп - его остановить.
% 03
Нашему классу ChatServer понадобится экземпляр потока сервера. И давайте его по нажатию старта создадим. Дадим ему какое-нибудь имя, типа SeverThread и передадим в него порт. sst = new sst("st", port). Пробуем, и у нас бесконечно выводится сообщение о том, что работает поток. Интерфейс не залип, что говорит нам о том, что сообщение выводится из какого-то ещё потока. Думаю, если вы попробуете сделать тоже самое вживую, а не просто посмотрите на лекцию, будет более наглядно.
% 04
Но и остановить мы этот другой поток не можем. Нас это не устраивает, поэтому мы идём использовать второй вариант остановки - помните, вызывать метод стоп нельзя? мы серверный поток зацикливаем не по true, а пока поток не прерван. а по кнопочке стоп мы серверу скажем - прервись, пожалуйста. Вот, собственно, сразу живой пример управления потоком. Пример, который мы будем использовать. Это единственный штатный правильный способ тормознуть поток. Мы его должны как-то изначально правильно написать, чтобы он время от времени проверял флажок, и если флажок сбросился - поток должен как-то сам подумать, как ему перестать работать. \\ \hline
09-13 & Чтобы на этот пример было приятнее смотреть, для начала разгрузим немного наш поток, чтобы он нам в логи не кидал постоянно сообщение о том, что он работает, вспомним, что у класса потока есть довольно много интересных методов, в том числе статических -- каждый поток можно заставить поспать. Например после вывода сообщения мы можем заставить наш поток спать какое-то время. Просто написав Тред.слип видим, что среда разработки нам наш метод подчеркнула. Метод sleep(3000) может выбрасывать InterruptedException. И вот этот InterruptedException выбрасывается, когда ваш поток спит, а ему кто-то снаружи делает interrupt. Обернём в трай-кэтч, залоггируем этот момент, и посмотрим, как это работает. Видим -- сообщение выводится каждые 3 секунды, всё хорошо, жмём кнопку остановки, сработал interrupt но флаг не сбросился и поток то не прервался.
% 02
Помните, я на лекции про исключения говорил, что самое плохое, что можно сделать с исключением -- это его проигнорировать или просто залоггировать? Вот конкретный случай, когда нельзя просто взять и залоггировать исключение, потому что мы не получаем ожидаемого поведения от потока. Нам надо либо самим сбросить флаг, либо ещё что-то сделать. Все прониклись проблемой? Получается, в кэтч поток может сам себе сделать приятно, мягко прервавшись, а в данном случае и это достаточно штатно -- можно брейкнуться из цикла, например, потому что после сна иногда ещё какие-то действия происходят. Для верности всё залоггируем и посмотрим, что при нажатии кнопки старта стартует поток, а при нажатии стопа поток останавливается. Оказалось не так уж и сложно. \\ \hline
09-14 & Какие у нас проблемы ещё остались? Мы можем сколько-то много раз нажать на старт и насоздавать потоков. Эти потоки будут создаваться, а остановить мы сможем только крайний, потому что ссылка у нас останется только на него. Ещё раз, внимательно, мы стартуем поток, ссылка на него сохраняется в идентификаторе, вызвав старт ещё раз мы не смотрим, существует ли ссылка на поток в идентификаторе, и всегда просто создаём новый поток. Предыдущий созданный поток -- просто продолжает существовать и работать параллельно, но никаких способов его прервать у нас нет. Чтобы быть уверенными, что поток будет создан только один -- мы можем спросить у потока, а вообще, существует и живой он, или нет. Для этого есть прекрасный метод isAlive(). Внимательно смотрим на 7ю строку метода, запускающего поток.
% 02
Вроде всё круто, но у нас же изначально ничего в этой переменной не лежит, значит если мы на кнопочку нажмём - отвалимся с NullPointerException. Дописываем вторую проверку на null, и когда нажмём кнопку мы либо просто сообщим, что поток уже запущен, а в противном случае -- стартуем новый сервер, в принципе, больше ничего не нужно делать.
% 03
Обратная ситуация -- когда мы не стартовали поток, а просто жмём стоп, допустим, по ошибке -- получим неприятность -- снова отвалимся с NullPointerException. И правильно сделаем, ведь у нас же объект сервера никогда не создавался, но и мы в нашем изучении идём от простого к сложному. Думаем, в каком случае нам не нужно прерывать поток, в случае нажатия кнопки стоп? Если потока не существует, или он уже не живой.
Получается, что если до старта жмём стоп, видим сообщение о том, что поток не запущен, стартуем, прерываем, и если ещё раз жмём стоп - в переменной не налл, но и поток не живой, так что пишет, что сервер не запущен. Конечно, ничего страшного не будет, если наш метод потыкат палочкой в мёртвый метод, но для порядка всё таки надо такие вещи ограничивать, потому что чаще всего мы хотим контролировать все аспекты существования объектов в системе. \\ \hline
09-15 & Ну как, увлекательно пока что работать с потоками? Предлагаю проверить, как в целом усваивается. По традиции у нас три вопроса. Многопоточность это возможность исполнять несколько программ параллельно; инструмент создания высоконагруженных приложений; инструмент создания иллюзии параллельности работы приложения.
... 30 сек...
Отчасти верно всё, но самое основное -- это создание иллюзии параллельности работы приложения. Несколько программ -- это многопроцессность, а высоконагруженность -- это то, что можно достигать в том числе многопоточностью, но не в первую очередь ею.
Для запуска отдельного потока исполнения приложения необходимо создать потомка класса Thread; переопределить метод run() класса Thread; переопределить метод start() класса Thread.
... 30 сек...
Переопределение ран задаёт поведение потока, переопределение метода старт не имеет смысла, а вот вызов метода старт делает то, что нужно -- запускает поток. И раз запускать мы поток умеем, то чтобы остановить выполниение потока необходимо для объекта потока выполнить метод Thread\#stop(); переопределить метод Thread\#interrupt(); выполнить метод Thread\#interrupt();
... 30 сек...
Стоп это запрещённый по понятным причинам метод, переопределение интеррапт не приведёт к нужному эффекту, а вот вызов метода интеррапт для объекта потока, предварительно верно спроектированным потоком -- то, что нужно. \\ \hline
09-16 & Вернёмся к нашему созданному для примеров MyThread. Прерывание потока мы разобрали. Поговорили о старте, о том как можно поспать, осталось поговорить ещё об одном моменте в части базового менеджмента потоков. О том, как можно дождаться завершения потока. На слайде видно, что у нас был некий поток мейн, который в какой-то момент своей жизни запустил на параллельное исполнение ещё четыре потока, создав асинхронность выполнения программы. Можно, конечно сходить в википедию за определением, но я думаю, лучше сказать своими словами. Асинхронность -- это такое явление, которое позволяет говорить о независимости исполнителей, в нашем случае потоков. То есть временная шкала у них едина, но совершенно нет гарантий того, что потоки выполнятся хотя бы приблизительно одновременно. мейн так и вовсе продолжит выполнять свои инструкции сразу, как только отдаст команды четырём параллельным потокам на запуск, на слайде это место отмечено словом «сейчас». Предположим ситуацию, в которой мейн отдал параллельным потокам какую-нибудь сложную задачу.
% 02
Вот, предположим, у нас есть какое-то очень-очень большое изображение, и нам надо с ним что-то сделать, скажем, осветлить или наложить виньетку. Если мы будем это делать в одном потоке, значит мы последовательно возъмём каждый пискель и применим к нему соответствующий фильтр с соответствующим коэффициентом, получим довольно сложный цикл со множеством математики. А разбив на потоки, мы одновременно будем обрабатывать 4 картинки размером 5000х5000, получив таким образом такое-же обработанное изображение, но гораздо быстрее. Конечно, не в 4 раза, потому что нужно время на разбиение изображения, на синхронизацию, на создание потока. Какой поток когда будет работать - разруливает операционная система, мы на это не можем ни повлиять, ни отследить, а вот если мы запросим у ОС больше потоков, мы можем быть уверены, что она нам будет давать чуть больше процессорного времени, чем если бы мы не запрашивали потоки.
% 03
С точки зрения кода, мы теперь знаем как спать, поэтому можем сделать вид, что наш поток что-то тяжёлое делает. В МайТрэд мы заставляем поток спать столько секунд, сколько передадим в конструктор, но будем предполагать, что это он на самом деле делает что-то жутко сложное, например, накладывает на изображение виньетку. Вот здесь, кстати, пожалуй, единственный случай, где можно исключение проглотить. То есть ничего интеррапт не изменит, нам всё равно надо доспать своё время. Конечно, в этом случае исключение вообще невозможно, потому что мы интеррапт и посылать-то не будем, но вот, редкий слуйчай, когда исключение можно игнорировать. И, для наглядности, после сна залоггируем завершение потока, а перед сном - залоггируем его старт.
Далее в мэйне запустим несколько, и в конце мэйна залоггируем завершение мэйна. Увидим, что у нас запустился мейн, запустил все остальные потоки и завершился, а побочные потоки продолжают делать свои важные дела ещё секунду, две, три и четыре, соответственно. Но ели бы мейн отдал побочным потокам на обработку части изображения, а потом их забирал обратно -- у нас бы ничего не заработало, потому что мейн то не дождался обработки изображения, завершился и более к активной деятельности не возвращался.
% 04
Так вот -- есть способ заставить текущий поток дождаться завершения какого-либо другого потока. делается это при помощи метода join(). Метод не даст завершиться потоку, к которому был присоединён другой поток, то есть, если мы начали что-то делать в параллельных потоках и просим мейн дождаться -- он просто будет спать, пока другие потоки работают. Мейн становится классическим начальником других потоков.
% 05
Этот метод тоже умеет генерировать InterruptedException. И генерится он если уже наш, текущий поток прерывают, а мы ждём завершения чьей-то работы. Логично, ведь если мы кого-то ждём, надо не просто прерываться, а что-то сделать с тем, кого мы ждём. Старуем и видим, что метод join действительно не отпускает наш мэйн пока не закончит свою работу второй, третий и четвёртый потоки, гарантируя последовательность событий. Это один из простейших и эффективных способов синхронизации потоков.
В приведённом примере видно, что я заставляю первый поток сразу джойниться к мейну, без создания дополнительного идентификатора, но строка закомментирована. Такое поведение иногда бывает удобно, но чаще всего приводит к тому, что работа значительно замедляется, потому что мы сначала дождёмся выполнения задачи нулевым потоком, а потом уже будем создавать первый и остальные. С такими конструкциями при работе в многопоточных средах желательно быть внимательнее.\\ \hline
09-17 & Если внимательно присмотреться, можно заметить, что у класса Thread есть конструктор, который на вход принимает некий странный Runnable(). А ещё сам тред тоже, оказывается, реализует Раннебл, это намекает нам на то, что раннебл -- это интерфейс. Пройдя прямо там, в коде, к документации на Runnable видим, что это и впрямь интерфейс, содержащий подозрительно знакомый метод run(), который мы усердно переопределяем последние несколько минут. Значит, делаем вывод, о том, что именно выполненное в методе run() и будет выполнено в отдельном потоке. Пазл сложился.
% 02
С точки зрения вызова, покажу на примере горячо всеми любимых анонимных классов. Я не великий поклонник таких этажерок, однако такие конструкции достаточно популярны и их можно довольно часто встретить в чужом коде. Как видно в выводе терминала в приложении создался новый поток безо всяких наследников майтред и прочих интересных сложностей. Сразу отвечу на невысказанный вопрос, когда использовать раннебл, а когда использовать тред. Если ваш отдельный поток должен хранить какое-то состояние внутри себя -- используйте тред, если вы запускаете какую-то задачу, работающую без хранения состояния -- используйте раннебл. \\ \hline
09-18 & Перейдём к сложным моментам. Потоки работают совершенно асинхронно. Переключаются в какие-то неизвестные моменты времени, и вообще никак друг от друга не зависят. Пока потоки работают с какими-то своими данными -- никаких проблем нет. Проблемы возникают, когда потоки начинают работать с общими данными. Два самых страшных слова для многопоточного приложения -- это Race Condition и Deadlock. \\ \hline
09-19 & Не будем далеко ходить. Простой пример, допустим в мэйне есть три переменных и некий метод incrementAll, который будет их как-то очень просто, но достаточно долго увеличивать обычным циклом. И в данной ситуации достаточно очевидно, что эти переменные должны увеличиваться равномерно и синхронно. Даже выведем их на экран по окончании цикла. Пока мы работаем в один поток - всё синхронно и замечательно. По сложившейся у нас неплохой традиции, предлагаю, раз оно так хорошо работает, всё сломать. Создадим вместо простого вызова метода Runnable который будет в своём методе run() делать одну вещь - вызывать наш очень хороший метод. И создадим пару новых потоков на его основе, и запустим их.
% 02
В одном потоке, даже если это будет не поток мейн, проблем не будет, мы также досчитаем до миллиона. Усложним задачу и создадим два потока. Тут сложность в том, что потоки начинают работать с одними и теми-же данными по очереди в определяемом операционной системой порядке и количестве времени. Получается полностью асинхронное исполнение. Запускаем, возможно, даже несколько раз и смотрим какие разные будут значения. Мы ни разу не получили значение 1 и 2 миллиона для первого и второго потока. Это называется "состояние гонки". Race Condition. Пожалуй одна из самых страшных вещей в многопоточном программировании. В этом рафинированном примере всё довольно очевидно, а если данных и их изменений поменьше, и потоков штук пять, то мы можем месяцами запускать программу, и она будет отрабатывать как надо, а потом бац и всё развалится, потому что потоки отработают не в том порядке.
% 03
Очевидно, что семантически нельзя допускать, чтобы этот метод выполнялся в разных потоках Это называется не потокобезопасный код. То есть никак нельзя двум потокам в этот цикл попадать. вообще никак нельзя. Почему? припоминаем, что класс хранится в куче вместе с его статическими переменными, а вот счётчик в цикле фор -- хранится на стеке, а значит изменяемые в цикле переменные -- общие для всех потоков, а счётчик у каждого потока свой. Но и тут есть подвох -- всё, что статическое и примитивное, на время исполнения и вычислений также помещается на стек, поэтому каждый поток может не успеть сохранить результаты своих вычислений, из-за этого и возникает гонка -- то значения обгоняют счётчик, то счётчик значения. На слайде я постарался показать, что примерно происходит в памяти, но важно помнить, что это очень приблизительный пример, в котором для простоты демонстрации используются равные промежутки времени работы потоков и счёт идёт до малых значений, в то время как в показанном ранее примере в коде мы видели реальный разброс значений. Можем немного внимательнее изучить этот слайд и проследить, когда и какие значения успели сохраниться в кучу, а какие нет.
\\ \hline
09-20 & Существуют способы синхронизации потоков. Так называемые "критические секции". Вы в коде выделяете критические секции
% 02
У каждой секции есть объект монитора.
% 03
К нашему методу прилетело несколько потоков, которые хотят его выполнить
% 04
Случайный поток, нам сложно предсказать какой, в зависимости от приоритета и ещё кучи факторов захватывается этим монитором
% 05
Поток отправляется в критическую секцию в которой существует наш метод и начинает его усердно выполнять. Остальные потоки встают в очередь и тупо ждут.
% 06
Как только первый поток выходит из критической секции он отпускает монитор, его захватывает какой-то другой случайный поток, и дело повторяется. Получается блокировка по монитору. Очень простой и очень надёжный механизм.
% 07
Естественно, что критических секций может быть много, и в каждой может быть как один метод, так и несколько, тут уж что напрограммируем.
% 08
Этот способ часто применяется не только для синхронизации потоков, но и для синхронизации процессов, например какая-то программа пишет лог файл, а другая программа должна читать лог файл, и она такая смотрит "аа, нельзя, только один процесс может файлик этот использовать"\\ \hline
09-21 & Отсюда возникает абсолютно логичный вопрос: откуда взять этот монитор. А очень просто, монитором может выступать совершенно любой объект. То есть это реализовано на уровне языка. А раз монитором может быть что угодно, осталось понять, как описать критическую секцию. Допустим мы хотим, чтобы в наш цикл внутри метода мог одновременно попасть только один поток, мы пишем ключевое слово synchronized() и в фигурные скобки оборачиваем критическую секцию. А в круглые скобки мы записываем объект, который будет выступать монитором. И всё. Вот так просто. Получился такой пример глючного кода, который мы почти вылечили синхронизацией. Почему почти? В результате запуска хорошо видно, что мы очень близко к нужному результату, но не достигли его.
Как вы думаете, почему результат именно такой? ... 30 сек...
% 02
На самом деле, первый поток завершил работу в критической секции на миллионной итерации цикла, но пока он переходил к следующей инструкции и формировал строку на вывод -- второй поток уже успел захватить монитор и немного изменить переменные, находящиеся в общей памяти, поэтому у первого потока получились неверные данные, а второму потоку уже никто не мешал и у него данные получились хорошие. Исправить ситуацию несложно -- синхронизировать не только цикл, но всё тело метода. Так мы логично подошли к ещё одной популярной конструкции.\\ \hline
09-22 & На самом деле, можно и не создавать отдельный объект. Для этого достаточно вспомнить, что вообще всё в джаве -- это объекты. даже классы, поэтому, возможно устроить синхронизацию по this. И ничего страшного, если монитором будет выступать текущий экземпляр, даже если мы не создавали его явно, его создаст виртуальная машина для своих служебных нужд или даже не экземпляр приложения, а сам класс программы. В данной ситуации мы не пускаем другие потоки во весь метод целиком, значит мы можем объявить весь метод как synchronized и это будет синхронизацией по this. Важно помнить, что если мы сделаем два синхронизированных по this метода, то у нас поток заберёт монитор одного из методов, и во второй метод тоже никто не попадёт. У них будет один монитор на двоих. Чудеса многопоточности, будьте внимательны.
% 02
Допустим, перед нами чужой код. Возможно, даже код стандартной библиотеки языка джава. Как понять, является-ли тот или иной метод потокобезопасным? Очень просто -- почитать документацию. берём любой метод, например, хорошо известный принтлн и читаем, если нигде не написано, что он Thread-safe - считаем его не потокобезопасным. И не можем к нему обращаться из разных потоков. Если вдруг внезапно нет документации под рукой - лезем в исходник и первое что видим - synchronized(this). Засинхронен. Явно потокобезопасный. Но если доков нет, исходников нет, и сами на первый взгляд мы оценить не можем - не имеем права считать метод потокобезопасным. \\ \hline
09-23 & Снова возникает логичный вопрос, почему бы не сделать вообще всё синхронизированным и потокобезопасным? Если всё объединять под один монитор, очевидно, что все потоки выстроятся в очередь и будет обычное последовательное исполнение программы, а всё объединить разными мониторами нельзя, потому что мы вполне можем попасть в ситуацию Dead Lock когда один поток будет ждать пока мы отпустим монитор одного метода, а в этом методе будет сидеть поток, который будет ждать, пока мы отпустим монитор первого метода. И всё, намертво. Таких ситуаций, конечно-же надо избегать, это результат неправильной архитектуры приложения. Внимательно присмотритесь к коду на слайде и проследите, кто кого ждёт и почему не может дождаться.
... 30 сек... \\ \hline
09-24 & \\ \hline
09-25 & \\ \hline
09-26 & \\ \hline
09-27 & \\ \hline
09-28 & \\ \hline
09-29 & \\ \hline
\end{longtable}
Ну и на сегодня предлагаю закончить. На мой взгляд многопоточность - это самое сложное, что есть в программировании вообще. С этим надо быть жутко аккуратным. Пока вы программируете в одном потоке - ваши баги будут повторяться при одинаковых условиях, а в многопоточных системах баг может годами не проявляться, а потом вдруг звёзды сошлись, потоки запустились в той последовательности, в которой им запускаться было не надо, случился DeadLock и всё, думай-гадай где он случился.
\end{document}
Когда Вы создаёте приложение на любой платформе у вас создаётся процесс, и как минимум создаётся один поток (мэйн). В джаве таких потоков для каждого процесса создаётся несколько - Main, Error, GC, EDT. Поскольку процессоры у нас ещё не очень хорошо работают с параллельной логикой - создаётся псевдо-параллельность, то есть какое-то количество времени исполняется один поток, какое-то другой, потом снова первый и т.д. И поскольку процессоры сейчас с бешеными гигагерцами, то для человека создаётся впечатление, что это происходит параллельно. Технология называется HyperThreading. Так вот, когда завершается процесс? Когда завершается последний поток. А дефолтный обработчик исключений висит не на процессе, а на потоке. Давайте сделаем так:
psvm
thr new RuntimeException("Hello from main");
Всё очевидно и прямолинейно, случилось исключение в главном потоке, уронили приложение, всё, как и планировалось. А теперь давайте внутри потока EDT попробуем упасть. Всё, что мы начали в этом потоке (конструктор) и всё что внутри конструктора описано - будет в этом потоке. А мэйн будет завершён. Чтобы было показательно - на какую-то кнопку повесим thr new RuntimeException("hello from EDT").
В потоке EDT исключения внезапно ведут себя по-другому. Оно происходит, выбрасывается, но приложение то не падает. Видите, жмём на кнопку, он выбрасывает исключение, и мы всё равно можем нажать на кнопку снова.
Это всё консоль, среда разработки, понятно, мы всё видим. А как вы думаете, что будет, если мы создадим jar файл, и запустим наше приложение отдельно от среды разработки? Вариантов может быть масса, а вы как думаете? Проще всего взять и проверить. (OpenModuleSettings - Artifacts + JAR + fromDependencies - ServerGUI). Делаем ставки. Я думаю тут два варианта - ничего не произойдёт, или упадёт. Второе - лучше. Надо объяснять почему? (что-то в приложении произошло, а мы не увидели). Прониклись проблемой?
Есть третий способ создания потоков - через ExecutorService. Он позволяет быстро создавать потоки, отдавать им задачи и не терять время на подготовку потока к работе, но об этом на следующем занятии. Если вкратце то Вы сразу создаёте некий пул потоков и они уже где то рядом с программой существуют, ожидая задачи и запуска.
Бывают SingleThreadPool с одним потоком,
FixedThreadPool с фиксированным количеством (если больше то ждём пока освободится),
и CachedThreadPool с заранее подготовленным количеством потоков, но умеющий расширять свой пул по необходимости. Явный его минус в том, что мы не можем ему указать потолок потоков. То есть если в какой то момент у Вас прилетит 20к параллельных запросов то вы создадите 20к потоков, и никакие из них не поставите в очередь.
И соответственно не можете созданные потоки удалить, то есть после всплеска они будут висеть неиспользуемые, а потоки, которые висят в пулах считаются активными потоками программы, даже когда они уже отработали. Но об этом подробно на следующем занятии.
***** ExecutorService + Thread Pool's
Давайте посмотрим в целом обзорно на общую идею, как ими пользоваться.
Используем Executors в качестве фабрики для создания ExecutorService'ов
ExecutorService serv = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int w = 1;
serv.execute(new Runnable() {
@Override
public void run() {
System.out.println(w + "begin");
try {
Thread.sleep(100 + (int) (3000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(w + "end");
}
});
}
Скажем, что у нас есть только 4 потока, и 10 задач, обратите внимание, что задачи будут выполняться не более, чем по 4 в каждый момент времени.
А теперь вопрос: никто ничего странного не замечает? Приложение не завершилось. Это говорит нам о том, что потоки активны, и ожидают других заказов. То есть если бы потоки завершались и каждый раз стартовали по новой - это ничем не отличалось бы от обычного тред.старт. Поэтому надо всегда делать завершение сервиса
serv.shutdown();
Это говорит что надо завершить рботу сервиса когда все активности в потоках завершатся. по сути, это запрещает отдавать сервису новые задачи, и закрывает его когда выполнение задач заончится, а потоки будут уничтожены.
serv.awaitTermination();
ожидание окончания работы всех потоков, то есть по сути это что то вроде джоина для сервиса пулов.
***** Вспомним то что мы знаем
Все ли помнят что такое .join();
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Runnable");
}
};
Thread ex1 = new Thread(r);
Thread ex2 = new Thread(r);
try {
ex1.join();
ex2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Берём код и вопрос: Одновременно-ли выполнятся джоины? (как только первый джоин отпустит мы пойдём во второй). То есть он будет работать в том самом методе, в котором вызван и ждать потока, для которого вызван.
***** Про синхронизацию помним?
оего рода блокировка каждого потока и ожидание пока многопоточное приложение отпустит тот или иной метод. Что будет выведено в результате работы этого кода?
Counter c = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
c.inc();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
c.dec();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
try {
t1.start();
t2.start();
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("counter = " + c.getC());
Верно говорить, мы не знаем, потому что в методы инк и дек мы будем заходить в неконтролируемые многопоточные отрезки времени. Происходит так называемое состояние гонки (race condition) Возникает оно когда два отдельных потока пытаются постучаться в одну и ту же ячейку памяти, а операции инкремента и декремента не атомарные, поэтому потоки сначала выполняют геттер, смотрят какое значение увеличивать или уменьшать, в это время другой поток отнимает доступ и смотрит на значение которое увеличивать или уменьшать ему, ну и они соответственно делают это для собственных значений. Тут и нужна синхронизация, которая не даст двум потокам одновременно залезть в одну ячейку и изменить в ней значения.
Соответственно, добавив в методы синхронизацию мы гарантируем отсутствие таких ситуаций.
***** С помощью чего можно синхронизироваться
Когда мы пишем synchronized метод - что будет являться монитором? (объект у которого вызван метод) Тема мониторов не могла не рассматриваться на втором уровне в районе пятого урока. Какая важная особенность работы мониторов стоит того чтобы о ней упомянуть? то есть монитор пропускает в себя только один тред, поэтому если разные треды будут стучаться в разные методы, контролируемые одним монитором - они будут дожидаться, пока монитор отпустит тред.
Второй вариант - создать некоторый объект, который может быть монитором и внутри метода написать sunchronized(obj){...} что нам это даст? Возможность например создать не синхронизированный участок метода, который будет выполняться с риском получения состояния гонки. Но такой подход нужен если нам надо дать возможность в рамках одного класса дать возможность параллельно исполнять разные методы.
Третий вариант - что выступает в роли монитора, ели мы ставим синхронизацию на статический метод? (сам класс). Соответственно, если есть статический синхронизированный метод и нестатический синхронизированный метод, их вызов в двух потоках будет работать параллельно или последовательно? (параллельно, статик по классу, нестатик по объекту этого класса).
***** Примерчики
У меня тут внезапно оказался примерчик набросаный по тредам и синхронизациям, можете его поразбирать, а если считаете, что он сложный - сотрите и никогда не открывайте. Суть в чём, четыре человека пытаются сеть на два стула, причём первые три пытаются сесть на первый стул, а четвёртый на второй. Увидим, что первые трое никогда не сядут на один стул одновременно, а четвёртый спокойно сядет на второй стул и спокойно с него сойдёт. Не удастся найти момент где два человека сидят на одном стуле.
Смотрим на второй пример IntegerMonitor, и думаем, как выполнятся методы, последовательно, или параллельно? (параллельно) Почему? Проведём эксперимент - закомментим n++. стали работать последовательно. В чём причина? Причина в использовании класса Integer. Его объекты неизменяемы, так что когда мы инкрементируем мы создаём новый объект и кладём его по той же ссылке. Второй поток видит незанятый никем монитор и захватывает его. Получается, несмотря на то, что они все засинхронизировались на одну переменную - они захватили три совершенно разных объекта. А по примитиву мы засинхрониться не можем.
***** Dead Lock
Ситуация при которой два потока начинают ждать друг друга и никогда этого не дождутся. Есть такая интересная история, когда приходит программист устраиваться на работу, и ему задают последний вопрос "мы примем Вас на работу если Вы расскажете нам, что такое Deadlock" на что он отвечает "я расскажу что такое deadlock как только вы возьмёте меня на работу".
Итак на примере мы видим, что первый поток захватывает первый монитор, и через секунду захочет захватить второй. Параллельно с ним второй поток захватывает второй монитор и через секунду захочет захватить первый. Таким образом первый поток будет ждать второй монитор, чтобы отпустить первый, а второй поток будет ждать первый монитор, чтобы отпустить второй. И вот они будут друг друга ждать, и никогда не дождутся.
***** Остановка потока
допустим у нас есть поток, внутри которого есть бесконечный цикл, и мы этот поток стартуем. как его остановить? Что плохого в t.stop(), почему он deprecated? (потому что никто снаружи никогда не знает что там внутри потока происходит и в каком состоянии поток будет когда мы его остановим)
Хороший вариант это interrupt давайте напишем, но почему не прервалось? (потому что кажется надо как то цикл переработать - например поставить в условие !Thread.currentThread().isInterrupted()). так всё равно по очевидной причине не сработает, но флаг нам действительно надо проверять, внутри цикла можем поставить условие (if (Thread.curr.isInterr()) break;) Ключевое отличие от стопа в том, что поток внутри себя знает когда он останавливается. И всё равно поток не останавливается, что же такое, сломали джаву? А дело всё в этом самом InterruptException, он говорит о том, что пока нельзя поток остановить - кто то всё же пытается. Получается ситуация когда нам нельзя просто брать и не задумываясь делать e.printStackTrace. И нам надо постараться в этой обработке написать какую то логику, в которой будет умная остановка потока.
Когда мы будем смотреть на всякие сервисы интересные - там будут два шатдауна - один который просто остановит сервис, а второй который ещё и всем потокам посылает интеррапты. но это посмотрим позже.
***** Демоны
Демон-поток - это поток, который не предотвращает выход JVM, когда программа заканчивается, но поток все еще работает. Примером для потока демона является сбор мусора.
В принципе потоки демоны это такие потоки, которые в принципе не жалко потерять. Например запустил пользователь радио, мы ему отдали тред с трансляцией, ну а когда закрыл - то закрыл, нам ничего не надо с программой при этом делать.
Вопрос такой - вот есть поток Т, внутри себя он запускает ещё поток-демон. Если мы завершим поток Т, но сделаем мэйн бесконечным - демоны остановятся? Демон будет работать пока хотя бы один поток программы будет работать.
***** Wait Notify
Никогда не понятно, как правильно говорить об этих двух механизмах и в каком порядке, но как то надо. Давайте я попробую объяснить, а Вы потом скажете, понятно или не понятно.
Представляем, что у нас есть три потока, засинхроненных по одному монитору. Каждый поток запускает какой нибудь маленький цикл внутри которого печатает свою букву. Вопрос: если их запустить параллельно все три - каковы шансы, что в консоли мы увидим ABC-ABC-ABC-ABC-ABC? (По хорошему это где то в районе тысячной процента). Гораздо более вероятно если напечатается 5A-5B-5C, потому что довольно маленькие задачи и нет смысла дробить их на части многопоточностью.
Ну и идея вэйт-нотифая в следующем - каждый поток пытается засинхронизироваться по монитору, и если он видит, что сейчас не может напечатать свою букву - переходит в режим ожидания. Такая же ситуация со вторым и третьим потоком. Затем, когда мы всё таки находим поток, который напечатает свою букву - мы печатаем букву и будим все остальные потоки. Возникает вопрос - как же три потока смогли зайти в один монитор? мы же вроде бы вошли в монитор и не можем уже другим потоком в этот монитор войти. Так в чём получается смысл всех этих вэйтов? Когда мы вызываем у монитора метод вэйт - мы отправляем поток в сон, и высвобождаем монитор. А нотифайОл будит все потоки, привязанные к этому монитору. Обратите внимание, что тут не иф, а вайл, потому что в случае, если кто то разбудит этот поток снова не в его время - ему опять надо будет отправиться спать, а не печатать свою букву невовремя.
Обычно это используется для проверок и реализации очередей. Допустим у нас есть очередь, которую мы заполняем некоторыми объектами, и очередь будет у нас монитором, с другой стороны этой очереди объекты будут выкидываться с какой то другой скоростью. Это можно реализовать заставив производителя поспать миллисекунду, и проверить, не надо ли положить новый объект в очередь. Это весьма нагрузит процессор. Собственно, чтобы не тратить ресурсы мы заполняем очередь и говорим производителю ждать, а когда с другой стороны объект забрали - говорим нотифай. Стандартная ситуация паттерна producer-consumer.
Одиночный нотифай будит один поток, первый в очереди потоков. Но поскольку мы не знаем какой из потоков в очереди стоит - нельзя однозначно сказать какой проснётся. Поэтому это используется обычно только когда у нас есть два потока и не более. В документации также сказано что надо вэйты делать в цикле, потому что по какой тонепонятной причине поток может быть разбужен системой.
Сегодня будем продолжать говорить о всяких классах которые помогают нам упростить жизнь, чтобы напрямую не использовать всякие мониторы, а как то удобнее работать со многопоточностью.
***** На прошлом уроке
Мы с вами рассмотрели Single ThreadPool, FixedThreadPool, CachedThreadPool. Давайте подробно разбираться.
Для создания пулов потоков мы используем ExecutorService pool = Executors.newFixedThreadPool(4);
Когда мы создаём фиксированный пул мы указываем что он подготавливает для нас 4 потока. И считается, что они будут активны, пока я не сделаю shutdown. А когда они активируются? Когда я создаю пул, или когда я даю им задачи? Давайте попытаемся ответить на этот вопрос. Обратите внимание, что у класса Executors есть два метода для создания например фиксированных пулов, один просто принимает количество нужных потоков, а второй может принять так называемый ThreadFactory. Когда пулу потоков нужен новый поток он использует некую фабрику, для его создания. Вообще по умолчанию там работает стандартная фабрика, но мы можем подкинуть туда и свою. Сделаем это и увидим, что тут есть в интерфейсе один метод newThread который принимает раннебл, то есть задачу, и отдаёт таки тред. То есть самый простой вариант - это отдать оттуда return new Thread(r);
Давайте попробуем поделать что то более интересное
Thread t = new Thread(r);
t.setDaemon(true);
t.setPriority(8);
return t;
Получится, что все потоки, которые будут созданы в этом пуле будут выполнять свою задачу, станут демонами и будут иметь приоритет 8. Получается своего рода преднастройка. Тут то мы можем и посмотреть, когда же создаётся новый тред, уберём демона и выведем в консоль надпись о создании потока. Запускаем и как видите консоль пустая.
***** Пробуем заглянуть внутрь
pool.execute(() -> System.out.println("3"));
вызываем пять пулов и видим, что создалось 4 новых треда, и пятый вывод произошёл уже без создания. Видим что действительно не создаются потоки сразу, действительно используется некая фабрика, и действительно тредов получается фиксированное количество.
вызов метода shutdown() закроет все неактивные потоки, дождётся завершения активных и их тоже закроет
метод shutdownNow() закроет все неактивные потоки, а у активных вызовет interrupt();
проверка isTerminated() говорит о том, уничтожен ли пул. То есть является ли он шатдаун и без активных потоков
проверка isShutdown() говорит о том, является ли сервер в состоянии выключения
awaitTermination(). Если Вы хотите подождать чтобы работу закончил какой то один поток - вы делаете join, если хотите дождатьвся всех - вызываем этот метод.
execute даёт пулу задачу. Кроме этого есть вариант вызвать submit() если нам надо получить из потока результат. Раннебл это интерфейс с войд методом ран, а тут возвращается некий дженерик Фьюче.
Интерфейс Callable<T> это дженерик, который указывает какого типа данные будут возвращены в результате работы потока. Теперь вопрос - как же нам этот результат поймать?
Как мы могли заметить метод submit возвращает объект типа Future. Он нужен для хранения того, что вернёт метод. и мы можем у этого самого объекта взять и запросить результат методом result.get(). Ну вот, а что будет, если сделать Thread.sleep(5000). Если задача выполняется 5 секунд, как я могу получить результат уже на следующей строке. На самом деле тут произойдёт что то вроде джоина, то есть мы тут зависнем до того момента как нам не прилетит ответ от треда. ну и что также очень удобно - метод перехватывает все возникшие в процессе исполнения тредом задачи исключения. Соответственно предварительно мы можем проверить, отменён ли поток и завершена ли его задача методами isDone() и isCancelled().
***** invokeAll/Any
Допустим у Вас есть список каких то задач, и их очень много, вы можете создать коллекцию этих задач, пулл потоков постарается эти задачи раздать своим потокам, и на выходе у пула запросить список результатов выполнения этих задач в виде списка фьючерсов. или, например, есть задачи, они где то собраны и они совершенно равнозначны, поэтому мы можем их выполнять, например в фоне, по одной раз в 10 секунд, отдаём коллекцию тасков методу invokeAny() и он будет их потихоньку разгребать.
***** ScheduledExecutorService
Очень часто складываются ситуации, когда надо опрашивать какой нибудь сайт раз в 10 секунд, и для этого надо написать метод, который будет запускать поток, в котором будет делать опрос, потом спать. Или, например, сам метод должен вызываться раз в 10 секунд, и это уже другой поток с другим поведением, или опрос раз в 5 секунд каждую нечётную минуту, и это третий поток с третьим набором Тред.слип()ов. Вот чтобы упростить такие мелкие но муторные задачи и придумали ScheduledExecutorService. Для его создания также используется класс Executors#newScheduledThreadPool(4). и вот здесь есть несколько вариантов того как мы можем отдать сюда задачу: просто запланированную, запланированную с рейтом и запланированную с задержкой.
***** Методы запуска
Простая задержка. Мы говорим, что у нас есть задача, и мы хотим её выполнить, но не прямо сейчас, а через 50 минут, например, указываем задержку и единицу измерения задержки.
Добавление фиксдРейта - это значит. что задача будет не только сдвинута во времени, но также мы указываем через какие промежутки времени надо её повторять. Если задача будет выполняться дольше, чем установленный промежуток - никакого промежутка времени бездействия не получится, и задачи просто будут выполняться друг за другом, накапливая несостыковку по времени.
По большому счёту - фикстДелэй делает тоже самое но с небольшим отличием, которое позволяет нам не думать о накоплении времени перекрытия задач.
+____.==__.==__.==__.==__ FixedRate
0 10 20 30 40
+____.==_10_.==_10_.==_10_. FixedDelay
0 10 15 25 30 40 45
То есть либо через равные независимые промежутки времени, либо сервис смотрит чтобы между окончанием задачи и началом следующей были равные промежутки времени.
***** О коллекциях
Напоминаю, что методы и вообще данные бывают синхронизированные и несинхронизированные. Коллекции - не исключение (забавная получилась игра слов). Если вы попытаетесь взять например ArrayList и попытаетесь его модифицировать то с очень большой долей вероятности вы получите ConcurrentModificationException. Из названия очевидно что это что то связанное с неправильным параллельным изменением коллекции. В случае ArrayList у нас есть такая замена как Vector - это тот же самый ArrayList только он синхронизирован. Понятно, что мы не первые столкнулись с задачей хранения каких то данных в многопоточных приложениях, поэтому фрейморк коллекций предоставляет довольно много синхронизированных аналогов обычных коллекций. давайте на некоторые из них посмотрим. Полный список и интересное чтиво на ночь можно найти в пакете java.util.concurrent.*
****** CopyOnWriteArrayList
Про то что бывают синхронизированные эррэйлисты я сказал, это класс вектор, а ещё есть класс вот с таким сложным называнием которое намекает нам на то что он в глубине души - эррэйлист. Судя по тому, что находится он в пакете concurrent - заточен он под работу в многопоточных приложениях. С одной стороны это хорошо, вроде как избавляет нас от большого количества проблем, а с другой надо быть с этой коллекцией весьма осторожным - смотрим на название - Копировать-При-Записи. То есть все могут свободно читать из этой коолекции, но при любой модификации этого списка - будет сделана его копия. Зачем? Чтобы вообще никак не мешать тем, кто читает. То есть если предполагается большое количество записей - может получиться слегка мусорно.
****** ArrayBlockingQueue
Это естественно дженерик который можно использовать очевидно как очередь. Тут при создании надо указать размерность очереди, динамики как мы в качестве примера это делали на уроках по алгоритмам и структурам данных не будет. Интересная особенность в том, что мы можем класть в эту очередь элементы четырьмя разными методами, и убирать их из очереди также четырьмя разными методами.
add бросает исключение если очередь полная, а мы хотим добавить следующий элемент.
offer вернёт булево - добавилось значение в очередь или нет.
У оффера есть перегрузка с таймаутом, то есть вы можете сказать в течение какого времени метод должен пытаться класть в очередь значение, после чего вернёт своё булево, говорящее об успехе.
put вешает поток и ждёт пока место в очереди не освободится. То есть фактически поток переходит в режим ожидания свободной ячейки в очереди.
Для вывода элемента из очереди есть соответствующие четыре метода
remove - если в очереди нет объектов бросает исключение
poll с перегрузкой указывающей таймаут - либо вернёт объект, либо null. по таймауту аналогично
take фактически вешает поток который спрашивает данные из очереди, и не отпускает его до появления данных.
Если посмотреть внутрь методов, которые переводят потоки в режим ожидания, то там работает механизм вейт-нотифай, который мы рассматривали на прошлом уроке, получается, что эти методы постоянно шлют в очередь оповещалки о том, пора ли класть или забирать объекты из очереди.
****** HashMap
Там внутри есть некоторые корзины, в которые кладутся значения по хешам и ключам, и соответственно, если два потока захотят одновременно что то положить в одну из корзин внутри мэпа - вполне можно получить RaceContition. Что же делать, если я хочу чтобы с моим мэпом работало много-много потоков? мы делаем такую вешь, как ConcurrentHashMap<K, V>. Тут методы такие же как в обычном ХешМэп, пут и гет, но судя по названию, этот класс приспособлен к работе в многопоточных средах. Там внутри хитрая синхронизация, которая для чтения ничего не блокирует, а для записи блокирует только ту корзину, в которую будет производиться запись (но делает это и для чтения в том числе), то есть при очень удачном стечении обстоятельств, если внутри мэпа 16 бакетов, то мы можем работать с ним в 16 потоков. Бакеты назначаются системой и мы на это особо не влияем.
****** Collections.synchronizedMap()
Есть ещё один способ работы с мэпами - Map<String, String> m = Collections.synchronizedMap(new HashMap<>()); Что с ним? вроде синхронизация, вроде многопоточность, зачем было придумывать что то ещё? Созданный таким образом мэп совершенно не оптимизирован. то есть если кто то читает - весь мэп полностью заблокирован. Ничего не сломается, но будет очень медленно.
***** Попробуем придумать какую нибудь задачу
ExecutorService serv = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
serv.execute(() -> {
System.out.println("1");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("2");
});
}
serv.shutdown();
Что может быть плохого в такой конструкции? давайте представим, что вместо сна происходит доступ к каким то общим данным. Пусть, например, это будут модули, которые пишут очень большие файлы на флешки, одновременно. Один-два-три файла записать параллельно - не сложно, но если это будет сотня-две-три файлов то это может значительно снизить скорость, потому что ОС нужно будет постоянно переключаться с одной задачи на другую и на это будет уходить больше времени, чем мы выиграем от параллеьности. Такое вполне может случиться, потому что ОС требуется время чтобы переключить контексты. Получается у нас есть сервис из десяти потоков, но чтобы не напортачить со скоростью доступа - нам надо сказать, что задачей копирования файлов должны заниматься не больше допустим четырёх потоков. С помощью вэйт-нотифаев это сделать довольно сложно, отому что там надо будет заводить какие то счётчики, как то их сложно проверять, а это тоже драгоценное процессорное время. На помощь приходит такой интересный класс, как Semaphore smp = new Semaphore(4); и в конструкторе мы указываем сколько у нас будет одновременных доступов. Теперь чтобы применить это к нашему сервису - мы в начале раннебла говорим smp.acquire(); и таким образом захватываем семафор, по сути это что то вроде захода в блок синхронизации, только для 4х потоков. Каждый поток захватывает по монитору внутри семафора, и когда их не остаётся - новый поток ждёт пока семафор не освободится. Соответственно надо не забывать его освободить после того как мы поработали smp.release(); который вообще желательно ставить в файналли, потому что кто его знает, что может произойти в трае, мало ли не отпустим семафор
***** Потоков у нас может быт сколько угодно
и разбросаны они могут быть неизвестно где, так что надо как то уметь с ними со всеми справляться. Например, надо их все заджоинить. Та ещё задачка, ведь не факт, что мы их создавали и у нас есть на них ссылки. Чтобы все их поймать нам понадобится так называемая защёлка. CountDownLatch cdl = new CountDownLatch(10); и в этом виде она рассчитана на 10 щелчков. что это значит? запустим 10 хитро придуманных тредов которые просто будут печатать строки и жить они будут разное количество времени.
CountDownLatch cdl = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
int w = i;
new Thread(() -> {
System.out.println(w + "-1");
try {
Thread.sleep((int) (Math.random() * 5000));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(w + "-2");
}).start();
}
ссылок на эти потоки у нас нет, поэтому заджоинить их классическим способом не удастся. поэтому мы в конце выполнения потока делаем cdl.countDown(); что сократит количество оставшихся щелчков защёлки на один. А вот где то в мэйне или любом другом классе я могу сказать cdl.await(); что заставит поток дождаться пока все щелчки не закончатся. По сути мы получили джоин для десяти потоков. Внутри каждого потока мы сказали что надо произвести щелчок защёлкой, а мэйну сказали - жди, пока значение в защёлке не станет нулём или не сработает таймаут из перегруженного метода ожидания await(10, TimeUnit.HOURS). Соответсвенно защёлки это одноразовые объекты и для того чтобы перезапустить защёлку - надо её заново пересоздать.
***** Модульность
Бывают ситуации, когда у нас программа состоит из нескольких модулей, и нам надо дождаться, пока все они загрузятся, чтобы продолжать работу программы. Как бы нам так хитро засинхронизировать много разных элементов, если один может грузиться пол секунды, а другой двадцать секунд? Для проверки сделаем что то похожее на предыдущий пример
for (int i = 1; i < 11; i++) {
int w = i;
new Thread(() -> {
System.out.println(w + "-старт");
try {
Thread.sleep((int) (Math.random() * 5000));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(w + "-готов");
System.out.println(w + "-стоп");
}).start();
}
Как бы нам сделать так, чтобы все потоки одновременно написали "стоп"? Для таких задач существует так называемый циклический барьер. При создании которого Вы говорите на сколько потоков он рассчитан. И в потоке между готовностью и остановкой говорите cb.await(); Идея в том, что каждый поток делает эвейт и снижает цифру в барьере на один, и переходит в режим ожидания. Когда последний десятый поток переводит барьер в 0 - метод отпускает все остальные потоки и они идут дальше. Получается, что таки образом мы дожидаемся всех. Здесь важно то что класс цикличный, так что если вы дождётесь 10 потоков, а потом каким то 11-м сделаете эвейт, то этот подсчёт ожидания пойдёт по новой. То есть если мы поменяем цикл до 21 то увидим 10 разнородных запусков и одновременных эндов, и потом снова 10 разнородных запусков и одновременных эндов.
***** Блоки synchronized
Не всегда бывают удобны, потому что если какой то поток подошёл к такому блоку, ему некуда деваться, он будет сидеть и ждать пока там монитор отпустит секцию. Как альтернатива предлагается такая сущность, как замОк.
ReentrantLock rl = new ReentrantLock();
По сути они заменяют блоки синхронизации, допустим у нас есть два потока, в которых мы делаем синхронизацию при помощи замков
new Thread(() -> {
rl.lock();
System.out.println("1");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("2");
rl.unlock();
}).start();
new Thread(() -> {
try {
rl.lock();
System.out.println("3");
Thread.sleep(2000);
System.out.println("4");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
rl.unlock();
}
}).start();
В этом случае мы по идее никогда не должны увидеть одновременно 1 и 3. как если бы там была обычная синхронизация по монитору. Но тут есть такой сомнительный момент. Если блок синхронизации у вас всегда захватывается потоком, и им же отпускается, то замок вы можете захватить в одном месте, а отпустить вообще неизвестно где. Для того чтобы гарантированно отпускать замки желательно их класть в файналли. Кроме этого сомнительного плюса есть ещё один - метод tryLock(). Это попытка засинхронизироваться. То есть если замок занят - мы ничего не делаем, возвращаем фолс. а если мы туда зашли - то вернули тру и заняли блок синхронизации. Также можно пробовать захватить замок с таймаутом, то есть пытаемся 10 минут, если не удалось - перестаём пытаться. То есть если заменить лок на if(rl.tryLock()) то мы напечатаем только одну пару, а второй поток туда даже не полезет. или же мы говорим что второй поток у нас не так уж и торопится и может пытаться захватить секцию 5 ближайших секунд. if (rl.tryLock(5, TimeUnit.SECONDS))
То есть это один из механизмов которые могут спасти вас от состояния DeadLock.
***** Ещё один вариант замка
ReentrantReadWriteLock
Внутри него живут несколько замков - это readLock() и writeLock() которые вы можете захватить и освободить независимо друг от друга. И в принципе вы пользуетесь ими не особо вникая как они устроены внутри. Идея в том, что вы можете работать с каким то ресурсом, читать и изменять его. Что может быть плохого в том, что пять тредов будут одновременно читать оттуда данные? (ничего) Соответственно, если кто то захочет что то в этот ресурс записать пока другие из него читают - надо ли запретить такое поведение? (да) И собственно так это и работает - не даст записывать пока кто то захватил замок чтения. и таких захвативших может быть сколько угодно, И обратная ситуация - пока кто то пишет - не даст читать оттуда данные, но писать может только кто то один.
А может ли сложиться ситуация, что мы хотим что то записать, ждём пока ресурс освободят читающие, и пока мы ждём туда прилетят другие читающие, и мы будем бесконечно ждать?
Замок довольно умный и не допустит такой ситуации, потому что поставит всех следующих после писателя читателей в очередь. За счёт такой нехитрой логики мы получаем значительное ускорение параллельного чтения и защиту от битых данных за счёт того что писать может только один.
Вообще внутри там довольно тяжёлая логика, но к счастью у нас есть готовая оболочка которая ведёт себя известным нам образом.
Использование замков естественно по ситуации, обычно в веб-приложениях высоконагруженных сервисах где сотни и тысячи потоков ломятся поработать с одним ресурсом.
***** Атомарные типы данных
Все же помнят как мы на втором уровне брали переменную, и многопоточно её ломали, одновременно что присваивая и читая. Все проблемы рэйс кондишна происходят из-за того что операция изменения она состоит из двух частей - чтения и записи, и мы не знаем в какой момент поток прервётся другим. Атомарные типы данных это те, у которых операция их изменения - неожиданно атомарная.
AtomicInteger ai = new AtomicInteger(100);
System.out.println(ai.decrementAndGet());
Соответственно мы можем гарантировать что это изменение точно не будет прервано никаким другим потоком. ну и многие другие методы которые в основном дублируют поведение привычное по математическим операторам. и некоторые другие, например compareAndSet() - мы говорим что ожидаем увидеть там значение 100. и еслитам действительно 100 то мы положим туда число из второго аргумента.

View File

@ -17,9 +17,9 @@
belowskip=3mm,
showstringspaces=false,
columns=flexible,
basicstyle=\scriptsize\ttfamily,
basicstyle=\larger[-1]\ttfamily,
numbers=left,
numberstyle=\tiny\color{gray},
numberstyle=\larger[-2]\color{gray},
keywordstyle=\color{blue},
commentstyle=\color{dkgreen},
stringstyle=\color{mauve},

View File

@ -22,6 +22,7 @@
\usepackage{spreadtab}
\usepackage{svg}
\usepackage{afterpage}
\usepackage{relsize}
\newcommand{\tabitem}{~~\llap{\textbullet}~~}
@ -48,6 +49,7 @@
\newcommand{\info}{\cellcolor{green!20}{\Huge \faInfoCircle \quad}}
\newcommand{\excl}{\cellcolor{red!20}{\Huge \faExclamationTriangle \quad}}
\newcommand{\quot}{\cellcolor{blue!20}{\Huge \faQuoteLeft \quad}}
\makeatletter
\newcommand{\setword}[2]{%