lab 2 java ======= Main.java ```java import java.util.Random; public class Main { public static void main(String[] args) { Stack stack = new Stack(); Thread producer1 = new Thread(new LatinLetterProducer(stack)); Thread producer2 = new Thread(new LatinLetterProducer(stack)); Thread killer1 = new Thread(new KillerSymbolProducer(stack)); Thread killer2 = new Thread(new KillerSymbolProducer(stack)); producer1.start(); producer2.start(); killer1.start(); killer2.start(); } } class LatinLetterProducer implements Runnable { private Stack stack; private Random random = new Random(); public LatinLetterProducer(Stack stack) { this.stack = stack; } @Override public void run() { while (true) { int count = random.nextInt(4) + 2; // Произвести от 2 до 5 символов for (int i = 0; i < count; i++) { char letter = (char) (random.nextInt(26) + 'a'); // Случайная латинская строчная буква try { stack.push(letter); // System.out.println(Thread.currentThread().getName() + " produced " + letter); // stack.printStack(); Thread.sleep(random.nextInt(1000)); // Случайная задержка перед следующим символом } catch (InterruptedException e) { e.printStackTrace(); } } } } } class KillerSymbolProducer implements Runnable { private Stack stack; private Random random = new Random(); public KillerSymbolProducer(Stack stack) { this.stack = stack; } @Override public void run() { while (true) { int count = random.nextInt(2) + 1; // Произвести 1 или 2 символа-убийцы for (int i = 0; i < count; i++) { char killerSymbol = random.nextBoolean() ? '#' : '@'; // Случайный символ-убийца try { if (killerSymbol == '#') { stack.pop(); // System.out.println(Thread.currentThread().getName() + " produced " + killerSymbol + " and removed one symbol"); } else { stack.pop2(); // System.out.println(Thread.currentThread().getName() + " produced " + killerSymbol + " and removed two symbols"); } // stack.printStack(); Thread.sleep(random.nextInt(1500)); // Случайная задержка перед следующим символом } catch (InterruptedException e) { e.printStackTrace(); } } } } } ``` Stack.java ```java import java.util.concurrent.locks.*; public class Stack { private char[] stack; private int top = -1; private static final int CAPACITY = 15; private ReentrantLock lock = new ReentrantLock(); private Condition stackFullCondition = lock.newCondition(); private Condition stackEmptyCondition = lock.newCondition(); public Stack() { stack = new char[CAPACITY]; } public void push(char item) throws InterruptedException { lock.lock(); try { while (isFull()) { stackFullCondition.await(); } top++; stack[top] = item; System.out.print(Thread.currentThread().getName() + " push( "+ item + " ) "); printStack(); stackEmptyCondition.signalAll(); } finally { lock.unlock(); } } public char pop() throws InterruptedException { lock.lock(); try { while (isEmpty()) { stackEmptyCondition.await(); } char item = stack[top]; top--; System.out.print(Thread.currentThread().getName() + " pop() "+ item + " "); printStack(); stackFullCondition.signalAll(); return item; } finally { lock.unlock(); } } public String pop2() throws InterruptedException { lock.lock(); try { while (isEmpty() || top < 1) { stackEmptyCondition.await(); } char item1 = stack[top]; top--; char item2 = stack[top]; top--; System.out.print(Thread.currentThread().getName() + " pop2()"+ item1 + item2 + " "); printStack(); stackFullCondition.signalAll(); return "" + item1 + item2; } finally { lock.unlock(); } } public char top() throws InterruptedException { lock.lock(); try { while (isEmpty()) { stackEmptyCondition.await(); } return stack[top]; } finally { lock.unlock(); } } public boolean isEmpty() { return top == -1; } public boolean isFull() { return top == CAPACITY - 1; } public void printStack() { lock.lock(); try { if (isEmpty()) { System.out.println("Stack is empty."); } else { System.out.print("Stack: "); for (int i = 0; i <= top; i++) { System.out.print(stack[i] + " "); } System.out.println(); } } finally { lock.unlock(); } } } ``` Пример работы ``` > javac *.java ; java Main Thread-0 push( g ) Stack: g Thread-1 push( u ) Stack: g u Thread-2 pop() u Stack: g Thread-1 push( d ) Stack: g d Thread-3 pop2()dg Stack is empty. Thread-0 push( t ) Stack: t Thread-2 pop() t Stack is empty. Thread-0 push( e ) Stack: e Thread-3 pop() e Stack is empty. Thread-1 push( w ) Stack: w Thread-2 pop() w Stack is empty. Thread-0 push( r ) Stack: r Thread-1 push( o ) Stack: r o Thread-3 pop() o Stack: r Thread-0 push( n ) Stack: r n Thread-0 push( j ) Stack: r n j Thread-2 pop2()jn Stack: r Thread-0 push( y ) Stack: r y Thread-2 pop2()yr Stack is empty. Thread-0 push( f ) Stack: f Thread-1 push( o ) Stack: f o Thread-3 pop2()of Stack is empty. Thread-0 push( a ) Stack: a Thread-1 push( s ) Stack: a s Thread-1 push( i ) Stack: a s i Thread-2 pop2()is Stack: a Thread-0 push( s ) Stack: a s Thread-3 pop2()sa Stack is empty. Thread-0 push( t ) Stack: t Thread-1 push( y ) Stack: t y Thread-0 push( w ) Stack: t y w Thread-1 push( i ) Stack: t y w i Thread-1 push( k ) Stack: t y w i k Thread-1 push( x ) Stack: t y w i k x Thread-1 push( z ) Stack: t y w i k x z Thread-3 pop() z Stack: t y w i k x Thread-2 pop() x Stack: t y w i k Thread-1 push( d ) Stack: t y w i k d Thread-0 push( f ) Stack: t y w i k d f Thread-2 pop() f Stack: t y w i k d Thread-0 push( r ) Stack: t y w i k d r Thread-1 push( j ) Stack: t y w i k d r j Thread-3 pop2()jr Stack: t y w i k d Thread-2 pop2()dk Stack: t y w i Thread-1 push( u ) Stack: t y w i u Thread-3 pop() u Stack: t y w i Thread-0 push( g ) Stack: t y w i g Thread-2 pop2()gi Stack: t y w Thread-1 push( r ) Stack: t y w r Thread-1 push( r ) Stack: t y w r r Thread-3 pop2()rr Stack: t y w Thread-0 push( d ) Stack: t y w d Thread-1 push( b ) Stack: t y w d b Thread-1 push( a ) Stack: t y w d b a Thread-0 push( c ) Stack: t y w d b a c Thread-2 pop2()ca Stack: t y w d b Thread-0 push( c ) Stack: t y w d b c Thread-3 pop2()cb Stack: t y w d Thread-1 push( t ) Stack: t y w d t Thread-3 pop() t Stack: t y w d Thread-0 push( m ) Stack: t y w d m Thread-3 pop() m Stack: t y w d Thread-0 push( i ) Stack: t y w d i Thread-2 pop() i Stack: t y w d Thread-3 pop2()dw Stack: t y Thread-1 push( t ) Stack: t y t Thread-3 pop() t Stack: t y Thread-0 push( r ) Stack: t y r Thread-2 pop2()ry Stack: t Thread-0 push( e ) Stack: t e Thread-1 push( s ) Stack: t e s Thread-0 push( i ) Stack: t e s i Thread-3 pop2()is Stack: t e Thread-1 push( z ) Stack: t e z Thread-0 push( f ) Stack: t e z f Thread-1 push( g ) Stack: t e z f g Thread-3 pop() g Stack: t e z f Thread-0 push( o ) Stack: t e z f o Thread-2 pop() o Stack: t e z f Thread-0 push( z ) Stack: t e z f z Thread-0 push( c ) Stack: t e z f z c Thread-1 push( j ) Stack: t e z f z c j Thread-3 pop2()jc Stack: t e z f z Thread-0 push( e ) Stack: t e z f z e Thread-1 push( l ) Stack: t e z f z e l Thread-1 push( m ) Stack: t e z f z e l m Thread-0 push( u ) Stack: t e z f z e l m u Thread-3 pop2()um Stack: t e z f z e l Thread-2 pop() l Stack: t e z f z e Thread-0 push( i ) Stack: t e z f z e i Thread-1 push( z ) Stack: t e z f z e i z Thread-0 push( d ) Stack: t e z f z e i z d Thread-0 push( n ) Stack: t e z f z e i z d n Thread-3 pop2()nd Stack: t e z f z e i z Thread-2 pop() z Stack: t e z f z e i Thread-1 push( g ) Stack: t e z f z e i g Thread-0 push( y ) Stack: t e z f z e i g y Thread-2 pop() y Stack: t e z f z e i g Thread-3 pop() g Stack: t e z f z e i ``` # Проект 🤡 Класс `Main`: ``` 1. Создать экземпляр класса Stack. 2. Создать и запустить два потока LatinLetterProducer для производства случайных латинских строчных букв. 3. Создать и запустить два потока KillerSymbolProducer для производства символов-убийц. ``` Класс `LatinLetterProducer`: ``` 1. Получить ссылку на объект Stack в конструкторе. 2. В методе run(): - Бесконечно выполнять следующие шаги: a. Сгенерировать случайное число count от 2 до 5 (количество символов для производства). b. Повторить count раз: i. Сгенерировать случайную латинскую строчную букву. ii. Поместить сгенерированную букву в стек, используя метод push(). iii. Вывести информацию о произведенной букве и текущем содержимом стека. iv. Сделать случайную задержку перед генерацией следующей буквы. ``` Класс `KillerSymbolProducer`: ``` 1. Получить ссылку на объект Stack в конструкторе. 2. В методе run(): - Бесконечно выполнять следующие шаги: a. Сгенерировать случайное число count (1 или 2) для определения количества символов-убийц. b. Повторить count раз: i. Сгенерировать случайный символ-убийцу ('#' или '@'). ii. Если сгенерированный символ - '#': - Удалить один символ из стека, используя метод pop(). - Вывести информацию об удаленном символе и текущем содержимом стека. iii. Если сгенерированный символ - '@': - Удалить два символа из стека, используя метод pop2(). - Вывести информацию об удаленных символах и текущем содержимом стека. iv. Сделать случайную задержку перед генерацией следующего символа-убийцы. ``` Класс `Stack`: ``` 1. Инициализировать массив stack фиксированного размера CAPACITY для хранения символов. 2. Инициализировать указатель top в -1 (индекс верхнего элемента стека). 3. Создать объект блокировки lock для обеспечения потокобезопасности. 4. Создать условия stackFullCondition и stackEmptyCondition для координации потоков. Метод push(char item): 1. Получить блокировку lock. 2. Пока стек полон, ждать на условии stackFullCondition. 3. Увеличить указатель top на 1. 4. Поместить символ item в массив stack по индексу top. 5. Сигнализировать всем потокам, ожидающим на условии stackEmptyCondition. 6. Освободить блокировку lock. Метод pop(): 1. Получить блокировку lock. 2. Пока стек пуст, ждать на условии stackEmptyCondition. 3. Получить символ из массива stack по индексу top и сохранить его в переменной item. 4. Уменьшить указатель top на 1. 5. Сигнализировать всем потокам, ожидающим на условии stackFullCondition. 6. Освободить блокировку lock. 7. Вернуть символ item. Метод pop2(): 1. Получить блокировку lock. 2. Пока стек пуст или содержит менее двух элементов, ждать на условии stackEmptyCondition. 3. Получить два символа из массива stack по индексам top и top-1 и сохранить их в переменных item1 и item2. 4. Уменьшить указатель top на 2. 5. Сигнализировать всем потокам, ожидающим на условии stackFullCondition. 6. Освободить блокировку lock. 7. Вернуть строку, состоящую из item1 и item2. Метод printStack(): 1. Получить блокировку lock. 2. Если стек пуст, вывести сообщение "Stack is empty". 3. Иначе, вывести содержимое стека, начиная с индекса 0 до top. 4. Освободить блокировку lock. ``` Эти словесные "проекты" описывают алгоритмы и логику работы каждого класса и его методов. Они помогают понять последовательность действий и взаимодействие между классами и потоками в программе. # Проект 2 🤡 обновленные словесные "проекты" с учетом доступов, объявлений переменных и классов: Класс `Main`: ```java public class Main { public static void main(String[] args) { 1. Создать экземпляр класса Stack. 2. Создать и запустить два потока LatinLetterProducer для производства случайных латинских строчных букв. 3. Создать и запустить два потока KillerSymbolProducer для производства символов-убийц. } } ``` Класс `LatinLetterProducer`: ```java class LatinLetterProducer implements Runnable { private Stack stack; private Random random = new Random(); public LatinLetterProducer(Stack stack) { 1. Получить ссылку на объект Stack в конструкторе. } @Override public void run() { 1. Бесконечно выполнять следующие шаги: a. Сгенерировать случайное число count от 2 до 5 (количество символов для производства). b. Повторить count раз: i. Сгенерировать случайную латинскую строчную букву. ii. Поместить сгенерированную букву в стек, используя метод push(). iii. Вывести информацию о произведенной букве и текущем содержимом стека. iv. Сделать случайную задержку перед генерацией следующей буквы. } } ``` Класс `KillerSymbolProducer`: ```java class KillerSymbolProducer implements Runnable { private Stack stack; private Random random = new Random(); public KillerSymbolProducer(Stack stack) { 1. Получить ссылку на объект Stack в конструкторе. } @Override public void run() { 1. Бесконечно выполнять следующие шаги: a. Сгенерировать случайное число count (1 или 2) для определения количества символов-убийц. b. Повторить count раз: i. Сгенерировать случайный символ-убийцу ('#' или '@'). ii. Если сгенерированный символ - '#': - Удалить один символ из стека, используя метод pop(). - Вывести информацию об удаленном символе и текущем содержимом стека. iii. Если сгенерированный символ - '@': - Удалить два символа из стека, используя метод pop2(). - Вывести информацию об удаленных символах и текущем содержимом стека. iv. Сделать случайную задержку перед генерацией следующего символа-убийцы. } } ``` Класс `Stack`: ```java public class Stack { private char[] stack; // массив для хранения символов. private int top = -1; // указатель на верхний элемент стека. private static final int CAPACITY = 10; // емкость стека. private ReentrantLock lock = new ReentrantLock(); // объект блокировки для обеспечения потокобезопасности. private Condition stackFullCondition = lock.newCondition(); // условие для ожидания, когда стек полон. private Condition stackEmptyCondition = lock.newCondition(); // условие для ожидания, когда стек пуст. public Stack() { 1. Инициализировать массив stack фиксированного размера CAPACITY для хранения символов. } public void push(char item) throws InterruptedException { 1. Получить блокировку lock. 2. Пока стек полон, ждать на условии stackFullCondition. 3. Увеличить указатель top на 1. 4. Поместить символ item в массив stack по индексу top. 5. Вывести информацию о потоке методе и состоянии стека 6. Сигнализировать всем потокам, ожидающим на условии stackEmptyCondition. 7. Освободить блокировку lock. } public char pop() throws InterruptedException { 1. Получить блокировку lock. 2. Пока стек пуст, ждать на условии stackEmptyCondition. 3. Получить символ из массива stack по индексу top и сохранить его в переменной item. 4. Уменьшить указатель top на 1. 6. Вывести информацию о потоке методе и состоянии стека 7. Сигнализировать всем потокам, ожидающим на условии stackFullCondition. 8. Освободить блокировку lock. 9. Вернуть символ item. } public String pop2() throws InterruptedException { 1. Получить блокировку lock. 2. Пока стек пуст или содержит менее двух элементов, ждать на условии stackEmptyCondition. 3. Получить два символа из массива stack по индексам top и top-1 и сохранить их в переменных item1 и item2. 4. Уменьшить указатель top на 2. 5. Вывести информацию о потоке методе и состоянии стека 6. Сигнализировать всем потокам, ожидающим на условии stackFullCondition. 7. Освободить блокировку lock. 8. Вернуть строку, состоящую из item1 и item2. } public void printStack() { 1. Получить блокировку lock. 2. Если стек пуст, вывести сообщение "Stack is empty". 3. Иначе, вывести содержимое стека, начиная с индекса 0 до top. 4. Освободить блокировку lock. } public boolean isEmpty() { 1. Вернуть true, если top равен -1, иначе вернуть false. } public boolean isFull() { 1. Вернуть true, если top равен CAPACITY - 1, иначе вернуть false. } } ``` Теперь словесные "проекты" включают объявления переменных, доступы и структуру классов. Это дает более полное представление о реализации каждого класса и его методов.