Threads

Процеси и нишки

Мultiprocessing- две или повече програми, които се изпълняват "видимо" конкурентно под контрола на операционната система. Програмите нямат никаква връзка помежду си освен факта че се стартират и изпълняват едновременно.

Прилага се от операционната система и в рамките на програмата не е необходимо да се взимат никакви мерки.

Multithreading - две или повече задачи, които се изпълняват "видимо" паралелно в рамките на една и съща програма. Понякога се наричат "леки" (lightweight ) процеси

Въпреки че се нуждае от поддръжката на операционната система, се прилага и изпълнява от самата програма. необходимо е специално проектиране и планиране на програмата.

Особености

Създаване

 

Класът java. lang. Thread позволява създаването на нови нишки (threads)
o
Всяка нишка трябва задължително да наследи интерфейса 
Runnable
        - Изпълнявания код е разположен в нейния метод  run()

o
2 метода за създаване на Thread :
    - 1) производен клас на  java. lang. Thread
            -- java. lang. Thread наследява Runnable
            -- трябва да се предефинира метода run()
    - 2) клас наследяващ интерфейса Runnable
            -- трябва да се дефинира метода run()

Под - клас на Thread

o Метод 1 : подклас на  Thread

class Proc1 extends Thread {
Proc1() {...} //
конструктор
  ...
  public void run() {
    ... //
дейност на нишката-безкраен цикъл?
  }
}
...

Proc1 p1 = new Proc1();
// създаване нишка p1
p1.start(); // Стартиране на нишката и изпълнение на p1. run()

Наследяване на  Runnable

o Метод 2 : клас наследяващ Runnable

class Proc2 implements Runnable {
Proc2() { ...} //
Конструктор
...
public void run() {
... //
дейност на нишката
}
}
...

Proc2 p = new Proc2();
Thread p2 = new Thread( p);

...
p2.start(); // Стартира нишка, която изпълнява p. run()

Кой метод да се избере ?

o Метод 1 : под-клас на Thread
   
- когато класът не наследява вече друг клас (внимание : няма множествено наследяване)
   
- при приложения
o
Метод  2 : наследяване на  Runnable

   
- когато класът вече е производен
   
- при аплети

public class MyThreadApplet extends Applet implements Runnable {}

 

   

Състояния

Състоянието на нишката показва какво в момента тя върши и какво е в състояние да извърши. Тя може да бъде в 4 състояния: нова ( New ), работеща (Runnable), неработеща,блокирана(Blocked) и завършена (Dead ).

 
 

За всяко влизане в неработещо състояние, съществува специфичен и различен от останалите начин за връщане в работещо състояние. Всяко връщане работи само за съответния начин на влизане. Например ако тя е блокирана със sleep() връщането и в работещо състояние може да стане само след изтичането на определения брой милисекунди. Извикването на метода resume() няма да има ефект.

Metod isAlive()

Класът Thread притежава метод isAlive(), който се използва за проверка на състоянието на нишката:

Metod getState()

Release 5.0 Въвежда метода Thread.getState(). При извикването му се връща една от следните Thread.State стойности:

 


    Прост пример:

 //: CounterA.java
// Using the Runnable interface to turn the
// main class into a thread.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

 public class CounterA extends Applet implements Runnable {
        private int count = 0;
        private boolean runFlag = true;
        private Thread selfThread = null;
        private Button
                  onOff = new Button("Stop"),
                  start = new Button("Start");
        private TextField t = new TextField(10);
        private Label  l = new Label("Thread: no Thread counter yet");
       public void init() {
              add(t);
              start.addActionListener(new StartL());
              add(start);
              onOff.addActionListener(new OnOffL());
              add(onOff);
              add(l);
         }
         public void run() {
                 while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e){}
                     if(runFlag) {
                          t.setText(Integer.toString(count++));
                          l.setText("Thread: "+selfThread.getName());
                     }
                 }
          }
  

class StartL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
                if(selfThread == null){
                        selfThread = new Thread(CounterA.this);
                        selfThread.start();
                }
                runFlag = true;
        }
  }
  class OnOffL implements ActionListener {
          public void actionPerformed(ActionEvent e) {
                  runFlag = false;
          }
  }
  public static void main(String[] args) {
          CounterA applet = new CounterA();
          Frame aFrame = new Frame("CounterA");
          aFrame.addWindowListener(
                  new WindowAdapter() {
                            public void windowClosing(WindowEvent e) {
                                    System.exit(0);
                            }
                  }
           );
          aFrame.add(applet, BorderLayout.CENTER);
          aFrame.setSize(300,200);
          applet.init();
          applet.start();
          aFrame.setVisible(true);
    }
}

CounterA:


 

 
 
  Същият пример с много нишки:
   
 //: CounterPT.java
// If you separate your thread from the main
// class, you can have as many threads as you want.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

class Ticker extends Thread {
   private Button b = new Button("Toggle");
   private TextField t = new TextField(10);
   private int count = 0;
   private boolean runFlag = true;
   public Ticker(Container c) {
      b.addActionListener(new ToggleL());
      Panel p = new Panel();
      p.add(t); p.add(b); c.add(p);
   }
   class ToggleL implements ActionListener {
     public void actionPerformed(ActionEvent e) {
        runFlag = !runFlag;
    }
   }
   public void run() {
     while (true) {
       if(runFlag)
       t.setText(Integer.toString(count++));
       try {
           sleep(100);
       } catch (InterruptedException e){}
     }
   }
   public void stp() {
       runFlag = false;
   }
   public void restart() {
       runFlag = true;
   }
}

public class CounterPT extends Applet {
  private Button start = new Button("Start");
  private Button stop = new Button("Stop");
  private Button restart = new Button("Restart");
  private boolean started = false;
  private Ticker[] s;
  private boolean isApplet = true;
  private int size;
  public void init() {
  // Get parameter "size" from Web page:
     if(isApplet)
     size = Integer.parseInt(getParameter("size"));
     s = new Ticker[size];
     for(int i = 0; i < s.length; i++)
         s[i] = new Ticker(this);
     start.addActionListener(new StartL());
     add(start);
     stop.addActionListener(new StopL());
     add(stop);
     restart.addActionListener(new RestartL());
     add(restart);
  }

  class StartL implements ActionListener {
     public void actionPerformed(ActionEvent e) {
         if(!started) {
             started = true;
             for(int i = 0; i < s.length; i++) s[i].start();
         }
     }
  }  
  class StopL implements ActionListener{
     public void actionPerformed(ActionEvent e) {
         for(int i=0;i<s.length;i++) s[i].stp();
     }
  }
  class RestartL implements ActionListener{
      public void actionPerformed(ActionEvent e){
          for(int i=0; i< s.length; i++) s[i].restart();
      }
  }
  public static void main(String[] args) {
      CounterPT applet = new CounterPT();
  // This isn't an applet, so set the flag and
  // produce the parameter values from args:
      applet.isApplet = false;
      applet.size = (args.length == 0 ? 5 :
           Integer.parseInt(args[0]));
      Frame aFrame = new Frame("CounterPT");
      aFrame.addWindowListener(
                 new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
                 System.exit(0);
          }
       });
       aFrame.add(applet, BorderLayout.CENTER);
       aFrame.setSize(200, applet.size * 50);
       applet.init();
       applet.start();
       aFrame.setVisible(true);
  }
}

CounterPT:

 

 Приоритети:
JVM управлява приоритетите чрез алгоритъм известен като fixed priority scheduling. 

Всеки  thread има приоритет (право да бъде стартирана преди другите). Приоритетите се представят с цели числа в диапазона от Thread.MAX_PRIORITY (10 - най-висок) до Thread.MIN_PRIORITY (1 - най-нисък). По подразбиране всяка нишка има същия приоритет като тази, която я създала. След като бъде създадена приоритета на нишката може да се променя с метода setPriority().

 
Дадена нишка може да бъде в работещо състояние само ако всички нишки с по-висок приоритет са или блокирани или завършени или не стартирани.

 
Ако няколко нишки очакват ресурс изборът се прави по round - robin метод. След като вземе ресурсите нишката ги освобождава при следните случаи:
  • Нишка с по-висок приоритет преминава в работещо състояние
  • Нишката преминава в блокирана с някой от методите
  • След изтичане на дадения и време интервал ако инсталираната JVM го осигурява.

Един прост пример
 

//: SimpleThread.java
public class SimpleThread extends Thread {
    private int countDown = 5;
    private String name;
    private static int threadCount =  0;
    public SimpleThread(String nameP) {
         name = nameP;
         System.out.println("Making " + name);
    }
    public void run() {
          for( ;countDown>0; countDown--) {
                System.out.println("Thread " + name + "(" + countDown + ")"+  
                           " priority -> " + getPriority() ););
          }
          System.out.println("Thread " + name + " end");
    }
    public static void main(String[] args) {
         String nameA[]={"Nick", "Marie", "George", "Isabelle", "Pierre"};
         for(int i = 0; i < 5; i++)
                new SimpleThread(nameA[i]).start();
          System.out.println("All Threads Started");
    }
}

 

Making Nick
Making Marie
Making George
Making Isabelle
Making Pierre
All Threads Started
Thread Marie(5) priority -> 5
Thread Marie(4) priority -> 5
Thread Marie(3) priority -> 5
Thread Marie(2) priority -> 5
Thread Marie(1) priority -> 5
Thread Marie end
Thread Isabelle(5) priority -> 5
Thread Isabelle(4) priority -> 5
Thread Pierre(5) priority -> 5
Thread Nick(5) priority -> 5
Thread George(5) priority -> 5
Thread Isabelle(3) priority -> 5
Thread Pierre(4) priority -> 5
Thread Nick(4) priority -> 5
Thread George(4) priority -> 5
Thread Isabelle(2) priority -> 5
Thread Pierre(3) priority -> 5
Thread Nick(3) priority -> 5
Thread George(3) priority -> 5
Thread Isabelle(1) priority -> 5
Thread Pierre(2) priority -> 5
Thread Nick(2) priority -> 5
Thread George(2) priority -> 5
Thread Isabelle end
Thread Pierre(1) priority -> 5
Thread Nick(1) priority -> 5
Thread George(1) priority -> 5
Thread Pierre end
Thread Nick end
Thread George end

 

 

Пример с модификация на приоритета:
//: SimpleThreadPr.java
public class SimpleThreadPr extends Thread {
   private int countDown = 5;
   private String name;
   private static int threadCount = 0;
   private volatile double d=0; // no optimization
   public SimpleThreadPr(String name, int prior) {
      this.name = name;
      setPriority(prior);
      System.out.println("Making " + name);
   }
   public void run() {
      for( ;countDown>0; countDown--) {

           // An expensive, interruptable operation:
          for(int i = 1; i < 100000; i++)
             d = d + (Math.PI + Math.E) / (double)i;

          System.out.println("Thread " + name + "(" + countDown + ")"+
               " priority -> " + getPriority() );
      }
      System.out.println("Thread " + name + " end");
   }
   public static void main(String[] args) {
      String nameA[]={"Nick", "Marie", "George", "Isabelle", "Pierre","Rose","Salome"};
      SimpleThreadPr st[] = new SimpleThreadPr[nameA.length];
      for(int i = 0; i < nameA.length; i++)
      st[i] = new SimpleThreadPr(nameA[i],i<3?Thread.MAX_PRIORITY:
                                                                       Thread.MIN_PRIORITY);
      for(int i = 3; i < nameA.length; i++)
          st[i] .start();
      System.out.println("The Threads with low priority started");
      for(int i = 0; i < 3; i++)
         st[i] .start();
      System.out.println("All Threads Started");
   }
}

 

Making Nick
Making Marie
Making George
Making Isabelle
Making Pierre
Making Rose
Making Salome
The Threads with low priority started
Thread Nick(5) priority -> 10
Thread Nick(4) priority -> 10
Thread Nick(3) priority -> 10
Thread Nick(2) priority -> 10
Thread Nick(1) priority -> 10
Thread Nick end
Thread Marie(5) priority -> 10
Thread Marie(4) priority -> 10
Thread Marie(3) priority -> 10
Thread Marie(2) priority -> 10
Thread Marie(1) priority -> 10
Thread Marie end
All Threads Started
Thread George(5) priority -> 10
Thread George(4) priority -> 10
Thread George(3) priority -> 10
Thread George(2) priority -> 10
Thread George(1) priority -> 10
Thread George end
Thread Pierre(5) priority -> 1
Thread Isabelle(5) priority -> 1
Thread Rose(5) priority -> 1
Thread Salome(5) priority -> 1
Thread Isabelle(4) priority -> 1
Thread Pierre(4) priority -> 1
Thread Rose(4) priority -> 1
Thread Salome(4) priority -> 1
Thread Isabelle(3) priority -> 1
Thread Pierre(3) priority -> 1
Thread Rose(3) priority -> 1
Thread Salome(3) priority -> 1
Thread Rose(2) priority -> 1
Thread Pierre(2) priority -> 1
Thread Salome(2) priority -> 1
Thread Isabelle(2) priority -> 1
Thread Rose(1) priority -> 1
Thread Rose end
Thread Isabelle(1) priority -> 1
Thread Isabelle end
Thread Salome(1) priority -> 1
Thread Salome end
Thread Pierre(1) priority -> 1
Thread Pierre end