Java. Примеры решения задач на потоки выполнения

Примеры решения задач на потоки выполнения (Threads). Работа с файлами в потоках. Сортировка в потоках


Содержание


Поиск на других ресурсах:




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 ]

 


Связанные темы