Задания для самостоятельной работы .

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ

ВЫСШЕГО ОБРАЗОВАНИЯ

«КУРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»

 

 

Кафедра методики программного обеспечения
и администрирования информационных систем

 

Т.В. Белова

 

Программирование на Java

 

 

Лабораторная работа №10

 

КУРСК 2017


 

Предлагаемое пособие предназначено для применения в процессе изучения дисциплин «Введение в объектно-ориетированное программирование» направления подготовки 01.03.02 Прикладная математика и информатика, «Объектно-ориентированный анализ и программирование» направления подготовки 38.03.05 Бизнес-информатика, «Технологии и методы программирования» направления подготовки 10.03.01 Информационная безопасность. Методическое пособие посвящено изучению основ программирования на языке Java. Пособие содержит комплекс лабораторных работ, краткие теоретические сведения, методические рекомендации к выполнению работ и задания для самостоятельного выполнения.

 

Рецензенты: доцент кафедры МПИиИТ, канд. пед. наук И.Н. Гостева

                   доцент кафедры мат. анализа, канд. ф.-м. наук М.В. Кабанко

© Курский государственный университет, 2017


Лабораторная работа №10

 

Потоки выполнения. Классы Timer и TimerTask

 

Цель работы: научиться реализовывать многозадачность на основе потоков.

 

Теоретические сведения

Понятие потока

Поток представляет собой последовательный путь выполнения внутри программы. Одновременно могут выполняться несколько потоков. Каждый поток в Java создается и управляется объектом класса java.lang.Thread. Потоки создают асинхронную среду выполнения, позволяя одновременно выполнять разные задачи.

Существуют пользовательские потоки и потоки-демоны. До тех пор, пока работает пользовательский поток, JVM не завершается. Работа потока-демона регулируется средой выполнения: он останавливается, если не осталось ни одного действующего пользовательского потока.

Когда выполняется автономное приложение, автоматически создается поток для выполнения метода main(). Такой поток называется главным потоком (main thread). Все другие потоки, называемые дочерними, порождаются из главного, наследуя его статус пользовательского потока. Метод main() может завершиться, но программа будет выполняться, пока не завершатся все пользовательские потоки.

Для любого потока до того, как он будет запущен, можно установить статус либо потока-демона, либо пользовтельского потока с помощью метода setDaemon(boolean) класса T h read. Попытка изменения статуса после запуска потока возбуждает исключение IllegalThreadStateException. Установление статуса демона для всех дочерних потоков гарантирует, что приложение завершится, когда прекратит работу главный поток.

При запуске приложения с графическим интерфейсом автоматически создается специальный поток для отслеживания взаимодействия пользователя с интерфейсом. Этот поток продолжает работу после остановки главного потока.

Создание потока

Потоки в Java реализуются тремя способами.

Первый способ – реализация интерфейса Runnable из пакета java.lang.

Интерфейс Runnable имеет следующее определение, содержащее одно объявление прототипа метода:

public interface Runnable {

void run();

}

Поток, который создается на базе объекта, реализующего интерфейс Runnable, будет выполнять код, написанный в общедоступном методе run(). Поток завершается, когда метод run() заканчивает работу либо при выбросе неперехваченного исключения. Процедура создания потока на базе интерфейса Runnable выглядит следующим образом:

1. Класс реализует интерфейс Runnable, предоставляя метод run(), который будет выполнен потоком. Объект этого класса – это объект Runnable.

2. Создается объект класса T h read, причем объект Runnable передается в качестве аргумента в конструктор T h read. Теперь объект T h read содержит в себе объект Runnable, который реализует метод run().

3. У объекта T h read, созданного на предыдущем шаге, вызывается метод start(). Метод немедленно возвращает управление после того, как поток будет создан.

Метод объекта run() в итоге выполняется потоком, представленным объектом Thread, метод start() которого был вызван.

class RunnableExample implements Runnable {

public void run() {

    System.out.println("Это печатает дочерний поток");

}

}

 

public class ThreadMain {

 

       public static void main(String[] args) {

             RunnableExample ts1 = new RunnableExample();

              Thread ts = new Thread(ts1);

       ts.start();

    System.out.println("Это напечатал главный поток");

}

 

}

Ниже приведен пример создания потока на основе реализации интерфейса Runnable. В классе ThreadMain создается объект класса RunnableExample. Затем этот объект помещается в объект-контейнер класса Thread. Главный поток (метод main) запускает дочерний поток путем вызова метода start. Этот метод запускает метод run дочернего потока.

 

Конструкторы класса Thread:

Thread ( Runnable threadTarget);

Thread ( Runnable threadTarget, String threadName);

Аргумент threadTarget – это объект, метод run() которого будет выполняться, когда запустится поток. Аргумент t h readName можно указать, чтобы дать явное имя для потока, а не использовать имя, сгенерированное автоматически.

Методы класса T h read:

Метод Описание
static Thread currentThread(); Возвращает ссылку на объект T h read выполняющегося в настоящее время потока.
final String getName(); Возвращает имя потока.
final void setName(String name); Устанавливает имя потока.
void run(); Этот метод должен быть переопределен подклассами класса T h read.
final void setDaemon(boolean flag); Устанавливает статус потока либо как демон, либо как пользовательский.
final boolean isDaemon(); Возвращает true, если поток является демоном.
void start(); Порождает новый поток, т. е. новый поток начинает выполняться как дочерний для текущего потока.

Второй способ – расширение класса Thread из того же пакета.

Типичная процедура для создания потока расширением класса выглядит следующим образом:

1. Класс, расширяющий класс T h read, переопределяет метод run() класса T h read, чтобы определить код, выполняемый потоком.

2. В этом подклассе можно вызвать явно конструктор класса T h read, чтобы проинициализировать поток, с помощью вызова super().

3. Чтобы сделать поток имеющим право на выполнение, у объекта класса вызывается метод start(), унаследованный из класса T h read.

Ниже приведен пример создания потока на основе расширения класса Thread. В классе ThreadMain создается объект класса ThreadExample. Затем главный поток (метод main) запускает дочерний поток путем вызова метода start. Этот метод запускает метод run дочернего потока.

 

class ThreadExample extends Thread {

    

public void run() {

    System.out.println("Это напечатал дочерний поток");

}

}

 

public class ThreadMain {

 

       public static void main(String[] args) {

             ThreadExample ts1 = new ThreadExample();

       ts1.start();

    System.out.println("Это напечатал главный поток");

}

 

}

 

Третий способ – реализация интерфейса Callable из пакета java.util.concurrent.

Возможность реализации интерфейса Callable появилась, начиная с версии Java 5. Интерфейс Callable является параметризованным, т.е. результатом выполнения потока может быть объект любого типа. Этот объект создается и возвращается методом call, который и запускает поток (обратите внимание, что метод run() в двух предыдущих способах имеет тип void). Еще одно преимущество интерфейса Callable – это возможность обработки исключений, не оказывая влияния на остальные потоки. Метод call может «выбросить» исключение с последующей его обработкой.

Ниже приведен пример создания задачи, предназначенной для параллельного выполнения, на основе реализации интерфейса Callable.

import java.io.IOException;

import java.util.concurrent.Callable;

class CallableExample implements Callable<String>{

public String call() throws Exception {

    if(какое-то условие) {

        throw new IOException("ошибка");

    }

    System.out.println("Это печатает дочерний поток");

    return "результат";

}

 }

Метод call этой задачи возвращает объект типа String.

Для запуска и остановки таких задач в программе надо создать объект типа ExecutorService и передать ему на выполнение задачу типа Callable. ExecutorService – это интерфейс, поэтому конкретная задача должна реализовать его. Однако пакете java.util.concurrent уже существуют готовые варианты реализации ExecutorService для определенных задач. Доступ к ним можно получить через статические методы служебного класса Executors.

Мы будем использовать метод newFixedThreadPool этого класса, который возвращает объект типа ExecutorService со встроенной возможностью создания пула потоков.

В объекте ExecutorService есть метод submit, который принимает на вход объект типа Callable или Runnable, а возвращает объект типа Future. Возвращаемый объект содержит результаты выполнения задачи. В частности, его метод isDone проверяет, завершилось ли выполнение задачи, а метод get возвращает результат или исключительную ситуацию, если в процессе выполнения задачи произошла ошибка.

Ниже приведен пример запуска задачи типа Callable и получения ее результатов.

import java.io.IOException;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

class CallableExample implements Callable<String>{

public String call() throws Exception {

    if(какое-то условие) {

        throw new IOException("error during task processing");

    }

    System.out.println("Это печатает дочерний поток");

    return "result ";

}

 }

 

public class ThreadMain {

 

 public static void main(String[] args) {

     //создать ExecutorService на базе пула из пяти потоков

    ExecutorService es1 = Executors.newFixedThreadPool(5);

    //поместить задачу в очередь на выполнение

     Future<String> f1 = es1.submit(new CallableExample());       

     while(!f1.isDone()) {

         //подождать, пока задача выполнится

     }

    try {

        //получить результат выполнения задачи

        System.out.println("task has been completed : " + f1.get());

    } catch (InterruptedException ie) {

    //выполнение потока прервано

        ie.printStackTrace(System.err);

    } catch (ExecutionException ee) {

        ee.printStackTrace(System.err);

    }

    es1.shutdown();

}

 

}

 

Пример 1

Написать программу, которая читает целые числа из файлов «numbers1.in», «numbers2.in» и «numbers3.in», считает сумму чисел в каждом из них и выводит в файл «numbers.out» информацию в виде <имя_файла>: сумма чисел.

Задачу подсчета суммы чисел в одном файле реализовать в виде отдельного потока.

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Scanner;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

 

import javax.swing.JOptionPane;

 

class SumNumbers implements Callable<String>{

String fileName;

SumNumbers (int i){

       fileName="D:numbers"+i+".in";

}

public String call() throws Exception {

int sum=0;

try {

   File fileWithNumbers = new File (fileName);

   Scanner scanner = new Scanner (fileWithNumbers);

   while (scanner.hasNext()){

         sum+=scanner.nextInt();

   }

   scanner.close();

   } catch (FileNotFoundException e) {

          throw new IOException("файл не найден");

   }

       return fileName+":"+sum;

    }

}

 

public class SumAllNumbers {

 public static void main(String[] args) {

        String result="";

    ExecutorService es1 = Executors.newFixedThreadPool(3);

          

   for (int i=1; i<=3; i++){

   Future<String> f1 = es1.submit(new SumNumbers(i));      

    while(!f1.isDone()) {

    }

   try {

           

       result+=f1.get()+" "   ;

   } catch (InterruptedException ie) {

   ie.printStackTrace(System.err);

   } catch (ExecutionException ee) {

       ee.printStackTrace(System.err);

   }

       

   }

   es1.shutdown();

   PrintWriter pw = null;

try{

   pw = new PrintWriter(new FileOutputStream("D:\\numbers.out"));

}

catch(FileNotFoundException e){

}

pw.println (result);

pw.close();

 

 }

}

 

Пример 2

Написать программу, рисующую в центре окна концентрические окружности.

import java.awt.*;

import java.util.*;

public class TimerExample extends Frame {

CirclePaint circlepaint = new CirclePaint();

   

Timer timer = new Timer();

int   x;

int   y;

int   r=10;

int   cnt=1;

public void paintcircle(){

cnt=cnt+1;

repaint();

}

public void paint(Graphics g ){

g.setColor(Color.red);

   for(int i=1;i<=cnt;i++){

     g.drawOval( x-r*i, y-r*i, 2*r*i, 2*r*i );

   }

}

public TimerExample() {

super("GraphicsExample");

       startTime();

       pack();

       setVisible(true);

       setSize(400,400);

}

void startTime() {

   timer.schedule( circlepaint, 1, 1000 );

}

class CirclePaint extends TimerTask {

   public void run(){

     paintcircle();

   }

}

public static void main(String[] args) {

       TimerExample win = new TimerExample();

       win.x=win.getWidth()/2;

       win.y=win.getHeight()/2;

}

public boolean handleEvent (Event event) {

       if (event.id==Event.WINDOW_DESTROY) {

       System.exit(0);

       return true;

       } else return false;

}

}

 

Задания для самостоятельной работы .

Задание 10.1

Выполнить задание 1 лабораторной работы №9. Программа должна читать данные из файлов «input1.in», «input2.in» и «input3.in», находить требуемую информацию и выводить в файл «output.out» сведения в виде <имя_файла>: информация.

Задачу поиска информации в одном файле реализовать в виде отдельного потока.

Задание 10.2

1. Создать приложение, в котором нарисован светофор с переключающимися через некоторый промежуток времени сигналами.

2. Создать приложение, в котором нарисован секундомер с движущейся стрелкой.

3. Создать приложение, заполняющее окно радужными полосами. Когда окно заполнится, цвет первой полосы меняется, и все начинается сначала.

4. Создать приложение, в котором случайным образом в окне появляется и исчезает квадрат, координаты и цвет фигуры меняются каждые 5 секунд.

5. Создать приложение, рисующее концентрические окружности случайного цвета, каждые пять секунд появляется новая окружность.

6. Создать приложение, в котором нарисованы часы с движущимися часовой и минутной стрелками.

7. Создать приложение, которое печатает бегущую строку: «Со времени запуска приложения прошло .. часов .. минут .. секунд».

8. Создать приложение, в котором вдоль границ окна движется квадрат, меняющий цвет.

9. Создать приложение: в центре нарисован квадрат, который постепенно увеличивается, изменяя цвет и заполняя все окно.

10. Создать приложение, постепенно рисующее квадрат в центре экрана. Когда квадрат нарисован полностью, он стирается, и приложение начинает рисовать другой квадрат, большего размера.

11. Создать приложение, которое рисует качающийся маятник.

12. Создать приложение, которое рисует работающие песочные часы.

13. Создать приложение, рисующее движущегося по прямой человечка.


Дата добавления: 2019-03-09; просмотров: 296; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!