Конкурирующие параллельные процессы


Объекты позволяют разбить программу на независимые секции.

Часто также необходимо превратить программу в несколько независимо выполняющихся подзадач.

Каждая такая подзадача называется процесс (другие способы перевода: нити или потоки, чтобы избежать путаницы с потоками (stream), в данной главе thread будет переводиться как процесс - Прим.перев. ).

Разбитая на подпроцессы программа выполняется так, как если бы каждый процесс был запущен сам по себе на отдельном процессоре.

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

Процесс - автономно выполняемая программа, запущенная в своем собственном адресном пространстве.

Многозадачная операционная система способна запускать более одного процесса (программы) одновременно, это выглядит так, как будто каждая выполняется сама по себе, за счет периодической передачи кванта времени процессора для каждой задачи.

Процесс есть простой последовательный поток управления в процессоре. Следовательно, один процессор может выполнять несколько конкурирующих процессов.

 

Любую программу можно написать без использования подпроцессов.

 

Однако, использование подпроцессов позволяет:

1.       Создавать «отзывчивый»  пользовательский интерфейс. Хороший пример этому кнопка "Выход" - не хотелось бы опрашивать ее состояние в каждом куске кода программы, но, в то же время, она должна реагировать на нажатие так, как будто регулярно проверяется.

 

2.      Повысить производительность программы.

 

3.     Упростить внутреннюю структуру программы.

 

 

Наследование от процесса

Простейшим путем для создания процесса является наследование от класса Thread, который имеет все необходимое для создания и запуска процесса.

Наиболее важный метод для Thread это run(), который необходимо переопределить чтобы процесс выполнял то, что вам необходимо. Таким образом, run() есть код, который будет запущен "одновременно" с другими процессами в программе.

Следующий пример создает произвольное количество процессов, отслеживаемые по присвоенному каждому процессу уникальному номеру, сгенерированному static переменной. Метод run()от Thread переопределен и выполняет уменьшение счетчика при каждом проходе цикла и завершает выполнение когда счетчик равен нулютом месте когда run() возвращает значение процесс завершается).

//: c14:SimpleThread.java
// Very simple Threading example.
 
public class SimpleThread extends Thread {
  private int countDown = 5;
  private static int threadCount = 0;
  private int threadNumber = ++threadCount;
  public SimpleThread() {
    System.out.println("Making " + threadNumber);
  }
  public void run() {
    while(true) {
      System.out.println("Thread " + 
        threadNumber + "(" + countDown + ")");
      if(--countDown == 0) return;
    }
  }
  public static void main(String[] args) {
    for(int i = 0; i < 5; i++)
      new SimpleThread().start();
    System.out.println("All Threads Started");
  }
} ///:~
 
 
The output for one run of this program will be different from that of another, because the thread scheduling mechanism is not deterministic.

 

 

Yielding

If you know that you’ve accomplished what you need to in your run( ) method, you can give a hint to the thread scheduling mechanism that you’ve done enough and that some other thread might as well have the CPU.

//: c13:YieldingThread.java
// Suggesting when to switch threads with yield().

public class YieldingThread extends Thread {
  private int countDown = 5;
  private static int threadCount = 0;
  public YieldingThread() {
    super("" + ++threadCount);
    start();
  }
  public String toString() {
    return "#" + getName() + ": " + countDown;
  }
  public void run() {
    while(true) {
      System.out.println(this);
      if(--countDown == 0) return;
      yield();
    }
  }
  public static void main(String[] args) {
    for(int i = 0; i < 5; i++)
      new YieldingThread();
  }
} ///:~

 

Sleeping

Another way you can control the behavior of your threads is by calling sleep( ) to cease execution for a given number of milliseconds.

//: c13:SleepingThread.java
// Calling sleep() to wait for awhile.

public class SleepingThread extends Thread {
  private int countDown = 5;
  private static int threadCount = 0;
  public SleepingThread() {
    super("" + ++threadCount);
    start();
  }
  public String toString() {
    return "#" + getName() + ": " + countDown;
  }
  public void run() {
    while(true) {
      System.out.println(this);
      if(--countDown == 0) return;
      try {
        sleep(100);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
    }
  }
  public static void   main(String[] args) throws InterruptedException {
    for(int i = 0; i < 5; i++)
      new SleepingThread().join();
  }
} ///:~

When you call sleep( ), it must be placed inside a try block because it’s possible for sleep( ) to be interrupted before it times out.

This happens if someone else has a reference to the thread and they call interrupt( ) on the thread (interrupt( ) also affects the thread if wait( ) or join( ) has been called for it, so those calls must be in a similar try block—you’ll learn about those methods later).

Usually, if you’re going to break out of a suspended thread using interrupt( ) you will use wait( ) rather than sleep( ), so that ending up inside the catch clause is unlikely.

Here, we follow the maxim “don’t catch an exception unless you know what to do with it” by re-throwing it as a RuntimeException.

Priority

The priority of a thread tells the scheduler how important this thread is.

Although the order that the CPU attends to an existing set of threads is indeterminate, if there are a number of threads blocked and waiting to be run, the scheduler will lean toward the one with the highest priority first.

However, this doesn’t mean that threads with lower priority aren’t run (that is, you can’t get deadlocked because of priorities).

Lower priority threads just tend to run less often.

The priorities are adjusting by using Thread’s setPriority( ) method.

 

Daemon threads

A “daemon” thread is one that is supposed to provide a general service in the background as long as the program is running, but is not part of the essence of the program.

Thus, when all of the non-daemon threads complete, the program is terminated.

Conversely, if there are any non-daemon threads still running, the program doesn’t terminate. There is, for instance, a non-daemon thread that runs main( ).

//: c13:SimpleDaemons.java
// Daemon threads don't prevent the program from ending.
 
public class SimpleDaemons extends Thread {
  public SimpleDaemons() {
    setDaemon(true);                        // Must be called before start()
    start();
  }
  public void run() {
            
  }
}

 

Coding variations

In the simple examples that you’ve seen so far, the thread objects are all inherited from Thread. This makes sense because the objects are clearly only being created as threads and have no other behavior.

However, your class may already be inheriting from another class, in which case you can’t also inherit from Thread (Java doesn’t support multiple inheritance).

In this case, you can use the alternative approach of implementing the Runnable interface. Runnable specifies only that there be a run( ) method implemented, and Thread also implements Runnable.

This example demonstrates the basics:

//: c13:RunnableThread.java
// SimpleThread using the Runnable interface.
 
public class RunnableThread implements Runnable {
  private int countDown = 5;
  public String toString() {
    return "#" + Thread.currentThread().getName() +
                                           ": " + countDown;
  }
  public void run() {
    while(true) {
      System.out.println(this);
      if(--countDown == 0) return;
    }
  }
  public static void main(String[] args) {
    for(int i = 1; i <= 5; i++)
      new Thread(new RunnableThread(), "" + i).start();
    // Output is like SimpleThread.java
  }
} ///:~