Примеры решения задач на потоки выполнения (Threads). Работа с файлами в потоках. Сортировка в потоках
Содержание
- 1. Задача. Параллельные целочисленные массивы в потоках
- 2. Задача. Запись массивов чисел в файлы в разных потоках. Реализация интерфейса Runnable
- 3. Задача. Чтение из файла массивов чисел
- 4. Задача. Поток строк типа String. Сортировка массива строк в нескольких потоках различными алгоритмами
- Связанные темы
Поиск на других ресурсах:
1. Задача. Параллельные целочисленные массивы в потоках
Условие задачи. Пользователь вводит с клавиатуры значение в массив. После чего запускаются два потока. Первый поток находит максимум в массиве, второй — минимум. Результаты вычислений возвращаются в метод main().
Решение. Данная задача решается с объявлением одного класса, в котором реализованы одновременно поиск минимального и максимального элемента массива. По желанию, можно реализовать два отдельных класса, которые будут определять соответствующие потоки.
// Класс, реализующий дочерний поток в задаче class ThreadMinMax implements Runnable { private Thread thr; // ссылка на дочерний поток private int[] AI; private int maximum; // максимальное значение private int minimum; // минимальное значение // Конструктор - получает массив целых чисел public ThreadMinMax(int[] _AI) { // Инициализация массива AI = _AI; // Создать поток thr = new Thread(this, "Thread1."); // Запустить поток выполнения thr.start(); } // Метод, в котором вписывается код выполнения потока // В нашем случае вписывается код поиска минимального значения // и заполнение переменной maximum. public void run() { int max = AI[0]; int min = AI[0]; for (int i=1; i<AI.length; i++) { if (max<AI[i]) max = AI[i]; if (min>AI[i]) min = AI[i]; } maximum = max; minimum = min; } // Методы доступа к полям класса public Thread getThread() { return thr; } public int getMax() { return maximum; } public int getMin() { return minimum; } } public class TrainThreads2 { public static void main(String[] args) { // 1. Объявить тестируемый массив int[] AI = { 2, 3, 4, 8, -1 }; // 2. Создать два дочерних потока, получить ссылки на них ThreadMinMax t1 = new ThreadMinMax(AI); ThreadMinMax t2 = new ThreadMinMax(AI); // 3. Прочитать результат try { // Ожидание завершения потоков t1, t2 - обязательно, // иначе можно получить нулевые значения t1.getThread().join(); t2.getThread().join(); } catch (InterruptedException e) { System.out.println("Error."); } // Прочитать результат после завершения потоков t1, t2 System.out.println("max = " + t1.getMax()); System.out.println("min = " + t1.getMin()); } }
Результат выполнения программы
max = 8 min = -1
⇑
2. Задача. Запись массивов чисел в файлы в разных потоках. Реализация интерфейса Runnable
Условие задачи. Заданы три целочисленных массива. Записать эти массивы в файл в паралельних потоках. Создать класс SaveAsThread для представления потока, который записывает целочисленный массив в файл.
Решение. Инкапсуляция потока реализована в классе SaveAsThread. Класс содержит следующие составляющие:
- AI — массив, который нужно записать в файл в потоке;
- filename — имя файла, в который записывается массив AI;
- threadName — имя текущего потока;
- t — ссылка на класс Thread, который реализует текущий поток;
- конструктор SaveAsThread(), который получает входными параметрами массив, имя файла и имя текущего потока;
- метод start() для запуска потока;
- метод run() в котором выполняется код потока. Этот метод запускается при вызове метода start() класса Thread по внутренней ссылке t.
import java.util.*; import java.io.*; // Класс, представляющий поток записи массива целых чисел в файл class SaveAsThread implements Runnable { private int[] AI; // записываемый массив private String filename; // внутреннее поле - имя файла private String threadName; // Имя потока private Thread t; // ссылка на текущий поток // Конструктор - получает 3 параметра: // - AI - массив, который нужно записать в файл; // - filename - имя файла, в который записывается массив AI; // - threadName - имя потока. public SaveAsThread(int[] AI, String filename, String threadName) { // Запомнить ссылку на массив this.AI = AI; // Запомнить имя файла this.filename = filename; // Запомнить имя потока this.threadName = threadName; // Создать поток с именем "SaveThread" t = new Thread(this, "SaveThread"); } // метод, запускающий текущий поток public void start() { t.start(); // вызвать метод run() } // В методе run() указывается код записи в файл public void run() { // Сообщить о начале выполнения потока System.out.println("Begin thread: " + threadName); try { // Создать связь с текстовым файлом filename: fOut -> filename FileOutputStream fOut = new FileOutputStream(filename); // Создать связь fOut: ps -> fOut -> filename PrintStream pS = new PrintStream(fOut); // Записать массив AI в файл pS.println(AI.length); for (int i=0; i<AI.length; i++) { pS.println(AI[i]); } // Закрыть потоки pS.close(); fOut.close(); } catch (IOException e) { // Вывести сообщение об ошибке System.out.println("Error: " + e.getMessage()); } // Сообщить о завершении потока System.out.println("End thread: " + threadName); } } public class Threads { public static void main(String[] args) throws IOException { // Запись массивов в разные файлы в разных потоках // 1. Создать три целочисленных массива int[] AI1 = { 2, 4, 3, 8, 9, 11, 7 }; int[] AI2 = { 1, 8, 7, 6, 3 }; int AI3[] = { 7, 7, 9, 9, 4, 2 }; // 2. Создать три потока SaveAsThread t1 = new SaveAsThread(AI1, "AI1.txt", "t1"); SaveAsThread t2 = new SaveAsThread(AI2, "AI2.txt", "t2"); SaveAsThread t3 = new SaveAsThread(AI3, "AI3.txt", "t3"); // 3. Запустить потоки на выполнение t1.start(); t2.start(); t3.start(); } }
Результат выполнения программы
Begin thread: t1 End thread: t1 Begin thread: t2 Begin thread: t3 End thread: t2 End thread: t3
После запуска программы будут созданы три файла с именами «AI1.txt», «AI2.txt», «AI3.txt», которые содержат значения элементов соответствующих массивов.
⇑
3. Задача. Чтение из файла массивов чисел
Данная задача является обратной по отношению к предыдущей задаче.
Условие задачи. Заданы три файла с именами «AI1.txt», «AI2.txt», «AI3.txt». В каждом из файлов записаны целочисленные массивы согласно следующему формату:
N number1 number2 ... numberN
здесь
- N – количество чисел, которые сохранены в файле;
- number1, number2, …, numberN – непосредственно числа.
Решение. Для решения задачи объявляется специальный класс ReadFileAsThread, который содержит следующие составляющие:
- AI – массив, который читается из файла;
- filename – имя файла, из которого нужно считать данные в массив AI;
- thr – ссылка на класс Thread. Данная ссылка указывает на текущий поток;
- конструктор, получающий входными параметрами имя файла и имя потока;
- метод Start() — предназначен для запуска потока из кода клиента. Данный метод вызывает метод start() класса Thread по ссылке thr. В результате вызывается метод run();
- метод get() — метод доступа к массиву AI из внешнего кода клиента (функции main()).
В другом классе Threads реализована функция main(), в которой продемонстрирована работа класса ReadFileAsThread. Создается 3 потока. Читаются массивы. Считанные массивы выводятся на экран.
Выполнение программы предусматривает наличие файлов «AI1.txt», «AI2.txt», «AI3.txt».
import java.util.*; import java.io.*; // Класс, инкапсулирующий поток чтения массива чисел из файла. // Класс расширяет возможности класса Thread class ReadFileAsThread extends Thread { private int[] AI; // массив, который нужно прочитать из файла private String filename; // имя файла private Thread thr; // ссылка на текущий поток // Конструктор public ReadFileAsThread(String filename, String threadName) { // Запомнить имя файла this.filename = filename; // Создать поток с именем threadName thr = new Thread(this, threadName); } // Запуск потока public void Start() { thr.start(); // Запускается метод run() } // Непосредственный код выполнения потока public void run() { // 1. Сообщить о начале выполнения потока - чтение из файла System.out.println("Begin thread: " + thr.getName()); FileInputStream fInput; try { // 2. Связать файл filename с экземпляром fInput: fInput <- filename fInput = new FileInputStream(filename); // 3. Связать экземпляр класса Scanner с экземпляром fInput: // scanner <- fInput <- filename Scanner scanner = new Scanner(fInput); // 4. Объявить дополнительные переменные int count; // Количество чисел в массиве // 5. Прочитать количество чисел в массиве count = scanner.nextInt(); // 6. Выделить память для массива AI AI = new int[count]; // 7. Прочитать числа for (int i=0; i<AI.length; i++) AI[i] = scanner.nextInt(); // 8. Закрыть потоки scanner.close(); fInput.close(); } catch (IOException e) { // Вывести сообщение об ошибке System.out.println("Error: " + e.getMessage()); } // Сообщить о завершении выполнения потока - чтение из файла System.out.println("End thread: " + thr.getName()); } // Метод доступа к массиву AI public int[] get() { return AI; } } public class Threads { public static void main(String[] args) throws IOException { // Запись массивов в разные файлы в разных потоках // 1. Создать три ссылки на целочисленные массивы int[] AI1 = null; int[] AI2 = null; int AI3[] = null; // 2. Создать три потока ReadFileAsThread t1 = new ReadFileAsThread("AI1.txt", "t1"); ReadFileAsThread t2 = new ReadFileAsThread("AI2.txt", "t2"); ReadFileAsThread t3 = new ReadFileAsThread("AI3.txt", "t3"); // 3. Запустить потоки на выполнение t1.start(); t2.start(); t3.start(); // 4. Дождаться завершения потоков чтобы получить корректный результат. // Для этого используется метод join() try { t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) { // Если ошибка, то вывести сообщение System.out.println("Error: " + e.getMessage()); return; } // 4. Прочитать массивы AI1 = t1.get(); AI2 = t2.get(); AI3 = t3.get(); // 5. Вывести массивы для контроля System.out.print("AI1 = [ "); for (int d : AI1) System.out.print(d + " "); System.out.println(" ]"); System.out.print("AI2 = [ "); for (int d : AI2) System.out.print(d + " "); System.out.println(" ]"); System.out.print("AI3 = [ "); for (int d : AI3) System.out.print(d + " "); System.out.println(" ]"); } }
Результат выполнения программы
Begin thread: t2 Begin thread: t1 Begin thread: t3 End thread: t3 End thread: t1 End thread: t2 AI1 = [ 2 4 3 8 9 11 7 ] AI2 = [ 1 8 7 6 3 ] AI3 = [ 7 7 9 9 4 2 ]
⇑
4. Задача. Поток строк типа String. Сортировка массива строк в нескольких потоках различными алгоритмами
Условие задачи. Реализовать следующие алгоритмы сортировки:
- сортировка вставками;
- сортировка выбором;
- сортировка пузырьком.
Продемонстрировать сортировки на массивах строк типа String[]. Каждый вид сортировки должен запускаться в отдельном потоке.
Решение. Для реализации алгоритмов сортировки, в программе реализованы три класса:
- SelectionSortThread — инкапсулирует поток, в котором осуществляется сортировка методом выбора;
- InsertionSortThread — инкапсулирует поток, в котором осуществляется сортировка методом вставки;
- BubbleSortThread — инкапсулирует поток, в котором осуществляется сортировка методом пузырька.
Все классы имеют схожие внутренние переменные и методы:
- AS — сортируемый массив;
- t — ссылка на текущий поток типа Thread;
- конструктор;
- метод start() – запускает поток на выполнение;
- метод run() – содержит код выполнения потока;
- метод get() – осуществляет доступ к внутреннему массиву AS;
- метод getThread() – необходим для доступа к потоку t типа Thread.
// Класс, который инкапсулирует поток, сортирущий строки // методом выбора в порядке убывания. class SelectionSortThread extends Thread { // Внутренние поля класса private String[] AS; // массив строк private Thread t; // ссылка на текущий поток // Конструктор, получает параметры: // - массив строк извне; // - имя текущего потока. public SelectionSortThread(String[] AS, String threadName) { // Сохранить ссылку на внешний массив this.AS = AS; // Создать поток t = new Thread(this, threadName); } // Метод, запускающий поток public void Start() { t.start(); // вызов метода класса Thread } // Метод, который выполняется в потоке public void run() { // Сортировать строки методом сортировки выбором // 1. Сообщить о начале сортировки System.out.println("Begin => " + t.getName()); // 2. Объявить дополнительные внутренние переменные int i, j, k; String s; // 3. Цикл сортировки выбором в порядке убывания строк for (i=0; i<AS.length; i++) { // i - текущий шаг k = i; // Поиск наибольшего (максимального) элемента s = AS[i]; for (j=i+1; j<AS.length; j++) if (AS[j].compareTo(s)>0) { k = j; // индекс максимального элемента s = AS[j]; } // Обменять местами максимальный элемент с AS[i] AS[k] = AS[i]; AS[i] = s; } // 4. Сообщить о завершении потока System.out.println("End => " + t.getName()); } // Метод доступа к массиву AS public String[] get() { return AS; } // Метод доступа к потоку t public Thread getThread() { return t; } // Метод, выводящий массив AS на экран public void Print() { System.out.print(t.getName() + " = [ "); for (String s : AS) System.out.print(s + " "); System.out.println(" ]"); } } // Класс, инкапсулирующий поток, в котором сортируется массив // строк методом вставки. Поток создается путем наследования класса Thread. // Сортировка происходит в порядке убывания элементов. class InsertionSortThread extends Thread { // Внутренние поля класса private String[] AS; // массив строк private Thread t; // текущий поток public InsertionSortThread(String[] AS, String threadName) { // Сохранить ссылку на внешний массив this.AS = AS; // Создать поток t = new Thread(this, threadName); } // Метод, который запускает поток из клиентского кода public void Start() { t.start(); // неявный вызов метода run() } // Метод, запускающий поток из клиентского кода public void run() { // Здесь нужно реализовать сортировку методом вставки // 1. Сообщить о начале сортировки System.out.println("Begin => " + t.getName()); // 2. Объявить внутренние переменные int i, j; String s; // 3. Цикл сортировки вставками for (i=0; i<AS.length; i++) { // i - номер прохода s = AS[i]; // Поиск места элемента в последовательности for (j=i-1; j>=0 && AS[j].compareTo(s)<0; j--) { // сдвинуть элемент вправо насколько возможно AS[j+1] = AS[j]; } AS[j+1] = s; } // 4. Сообщить о завершении сортировки (потока) System.out.println("End => " + t.getName()); } // Метод доступа к массиву AS public String[] get() { return AS; } // Метод доступа к потоку t public Thread getThread() { return t; } // Метод вывода массива AS на экран public void Print() { System.out.print(t.getName() + " = [ "); for (String s : AS) System.out.print(s + " "); System.out.println(" ]"); } } // Класс, инкапсулирующий поток, в котором происходит сортировка пузырьком. // Сортировка строк происходит в нисходящем порядке. // Класс реализует интерфейс Runnable class BubbleSortThread extends Thread { // Внутренние переменные класса private String[] AS; // массив строк private Thread t; // ссылка на текущий поток // Конструктор. Получает 2 параметра: // - массив строк; // - имя текущего потока. public BubbleSortThread(String[] AS, String threadName) { // Сохранить внешний массив this.AS = AS; // Создать новый поток t = new Thread(this, threadName); } // Метод, запускающий поток на выполнение public void Start() { t.start(); } // Метод, в котором выполняется поток public void run() { // Пузырьковая сортировка // 1. Сообщить о начале сортировки System.out.println("Begin => " + t.getName()); // 2. Объявить внутренние переменные int i, j; String s; // 3. Цикл сортировки for (i=0; i<AS.length; i++) { // i - номер прохода for (j=AS.length-1; j>i; j--) { // внутренний цикл прохода // сортировка по убыванию if (AS[j-1].compareTo(AS[j])<0) { s = AS[j]; AS[j] = AS[j-1]; AS[j-1] = s; } } } // 4. Сообщить о конце сортировки System.out.println("End => " + t.getName()); } // Метод доступа к массиву AS public String[] get() { return AS; } // Метод доступа к потоку t public Thread getThread() { return t; } // Метод, который выводит массив AS на экран public void Print() { System.out.print(t.getName() + " = [ "); for (String s : AS) System.out.print(s + " "); System.out.println(" ]"); } } // Класс, реализующий функцию main(), в которой тестируется // работа потоков public class Threads { public static void main(String[] args) { // Демонстрация сортировки массивов строк в разных потоках // 1. Объявить три массива строк. Массивы имеют одинаковые значения String[] AS1 = { "abc", "def", "bcd", "dsd", "aff", "jkl", "ffg"}; String[] AS2 = { "abc", "def", "bcd", "dsd", "aff", "jkl", "ffg"}; String[] AS3 = { "abc", "def", "bcd", "dsd", "aff", "jkl", "ffg"}; // 2. Объявить экземпляры классов потоков SelectionSortThread t1 = new SelectionSortThread(AS1, "t1:SelectionSort"); InsertionSortThread t2 = new InsertionSortThread(AS2, "t2:InsertionSort"); BubbleSortThread t3 = new BubbleSortThread(AS3, "t3:BubbleSort"); // 3. Запустить потоки t1, t2, t3 паралельно t1.Start(); t2.Start(); t3.Start(); // 4. Дождаться завершения потоков t1, t2, t3, чтобы получить корректный результат try { t1.getThread().join(); t2.getThread().join(); t3.getThread().join(); } catch (InterruptedException e) { // Если ошибка, то вывести сообщение System.out.println("Error: " + e.getMessage()); return; } // 5. Вывести отсортированные массивы на экран t1.Print(); t2.Print(); t3.Print(); } }
После запуска на выполнение программа выдала следующий результат
Begin => t2:InsertionSort Begin => t3:BubbleSort Begin => t1:SelectionSort End => t3:BubbleSort End => t2:InsertionSort End => t1:SelectionSort t1:SelectionSort = [ jkl ffg dsd def bcd aff abc ] t2:InsertionSort = [ jkl ffg dsd def bcd aff abc ] t3:BubbleSort = [ jkl ffg dsd def bcd aff abc ]
⇑
Связанные темы
- Многозадачность. Потоки выполнения. Основные понятия
- Средства Java для работы с потоками выполнения. Класс Thread. Интерфейс Runnable. Главный поток выполнения. Создание дочернего потока
- Методы класса Thread: getName(), run(), start(), sleep(). Примеры
- Методы класса Thread: join(), isAlive(), getPriority(), setPriority(). Примеры
⇑