Threads

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

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

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

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

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

Особености

Конструктори

Thread()
          Allocates a new Thread object.
Thread(Runnable target)
          Allocates a new Thread object.
Thread(Runnable target, String name)
          Allocates a new Thread object.
Thread(String name)
          Allocates a new Thread object.


Някои често използвани методи

public static Thread   currentThread()
          Returns a reference to the currently executing thread object.
 public final String     getName()
          Returns this thread's name.
public final void setName(String name)
          Changes the name of this thread to be equal to the argument name.



Създаване

 

Класът 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 javax.swing.*;

 public class CounterA extends JPanel 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 cnt = new CounterA();
          JFrame frame = new JFrame("CounterA");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
          frame.add(cnt);
          frame.setSize(300,200);
          cnt.init();
          frame.setVisible(true);
    }
}  


Или нишката да се убива и създава наново
//: CompteurA1.java
// Using the Runnable interface to turn the
// main class into a thread.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CounterA1 extends JPanel implements Runnable {
    private int count = 0;
    private Thread selfThread = null;
    private Button    stop = new Button("Stop"),
    start = new Button("Start");
    private TextField t = new TextField(10);
    private Label  l = new Label("Thread: no Thread counter yet");
    private boolean runFlag=true;
    public void init() {
        add(t);
        add(l);
        start.addActionListener(new StartL());
        add(start);
        stop.addActionListener(new StopL());
        add(stop);
    }
    public void run() {
        while (runFlag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e){}
            t.setText(Integer.toString(count++));
            l.setText("Thread: "+selfThread.getName());
        } 
        selfThread = null;
    }
    class StartL implements ActionListener {
         public void actionPerformed(ActionEvent e) {
             if(selfThread == null){
                 selfThread = new Thread(CounterA1.this);
                 runFlag=true;
                 selfThread.start();
             }
         }
    }
    class StopL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
             if(selfThread != null) {
                //  selfThread.stop();     deprecated
                 runFlag =false;               
             }
       }
    }
    public static void main(String[] args) {
        CounterA1 cnt = new CounterA1();
        JFrame aFrame = new JFrame("CounterA1");
        aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        aFrame.add(cnt);
        aFrame.setSize(300,200);
        cnt.init();
        aFrame.setVisible(true);
    }
}

 
 
 Същият пример с много нишки:

 //: 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 javax.swing.*;

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());
     JPanel p = new JPanel();
     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 JPanel {
  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 int size;
  public void init() {     
     this.setLayout(new FlowLayout()); 
     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 cnt = new CounterPT();

      cnt.size = (args.length == 0 ? 5 : Integer.parseInt(args[0]));
      JFrame aFrame = new JFrame("CounterPT");
      aFrame.add(cnt);
      aFrame.setSize(200*(1+cnt.size/10), 500);
      aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      cnt.init();
      aFrame.setVisible(true);
  }
}

    

 Упражнение: Променете програмата, така че след достигане на определена стойност нишката да завършва изпълнението си. Въведете бутон "New", който да създава нови нишки-броячи. Въведете програмата в страница - ExerciseMT.html

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

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

JVM използва два алгоритъма за превключване на нишките - време делене (Time sliced) известен като Round-Robin и изпреварващо (Pre-emptive). Как точно се управлява превключването зависи от платформата - използва се подхода използван в операционната система и е различен в различните случаи.

 Round - robin  е метод , който балансира натоварването. Планировчикът избира ресурс, посочен от брояч към списъка от нишки, след коет броячът се увеличава и ако е достигнат края, се завръща в началото на списъка. Методът предотвратяване опасността от "starvation" на нишка, тъй като всеки ресурс, подред се  избира от Планировчика. 

Изпреварващо (Pre-emptive) превключване позволява прекъсването на изпълнението на дадена нишка в зависимост от готовността на нишка с по-висок приоритет. 


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

    //: SimpleThread.java
import java.util.*;    // for Date
import java.text.*;    //for DateFormat and SimpleDateFormat
public class SimpleThread extends Thread {
    private int countDown = 5;
    private String name;
    private static Date d = new Date( );
    private static DateFormat df = new SimpleDateFormat("HH:mm:ss:SSS");
    private volatile double db=0; // no optimization
    public SimpleThread(String nameP) {
        name = nameP;
        System.out.println("\t\tMaking " + name);
    }
    public void run() {
        d.setTime(System.currentTimeMillis( ));
        System.out.println(name +  " start at " + df.format(d));
        for( ;countDown>0; countDown--) {
            System.out.println("Thread " + name + "(" + countDown + ")"+ 
                    " priority -> " + getPriority() );
            // An expensive, interruptible operation:
            for(int i = 1; i < 100000; i++)
                db = db + (Math.PI + Math.E) / (double)i;
        }
        System.out.println("Thread " + name + " end");
    }
    public static void main(String[] args) {
        d.setTime(System.currentTimeMillis( ));
        System.out.println("\t\tmain start at " + df.format(d));
        String nameA[]={"Nick", "Marie", "George", "Isabelle", "Pierre"};
        for(int i = 0; i < 5; i++){
            new SimpleThread(nameA[i]).start();           
        }
        System.out.println("\t\tAll Threads Started");
    }
}
        main start at 21:31:11:819
        Making Nick
        Making Marie
Nick start at 21:31:11:822
Thread Nick(5) priority -> 5
        Making George
Marie start at 21:31:11:822
Thread Marie(5) priority -> 5
        Making Isabelle
George start at 21:31:11:823
Thread George(5) priority -> 5
        Making Pierre
Isabelle start at 21:31:11:824
Thread Isabelle(5) priority -> 5
        All Threads Started
Pierre start at 21:31:11:825
Thread Pierre(5) priority -> 5
Thread Marie(4) priority -> 5
Thread Nick(4) priority -> 5
Thread George(4) priority -> 5
Thread Isabelle(4) priority -> 5
Thread Pierre(4) priority -> 5
Thread Marie(3) priority -> 5
Thread Nick(3) priority -> 5
Thread Isabelle(3) priority -> 5
Thread George(3) priority -> 5
Thread Pierre(3) priority -> 5
Thread Pierre(2) priority -> 5
Thread Marie(2) priority -> 5
Thread George(2) priority -> 5
Thread Isabelle(2) priority -> 5
Thread Nick(2) priority -> 5
Thread Pierre(1) priority -> 5
Thread Marie(1) priority -> 5
Thread George(1) priority -> 5
Thread Pierre end
Thread George end
Thread Marie end
Thread Isabelle(1) priority -> 5
Thread Nick(1) priority -> 5
Thread Isabelle end
Thread Nick end
  

 

 

Пример с модификация на приоритета:
import java.text.*;
import java.util.*;

//: SimpleThreadPr.java
public class SimpleThreadPr extends Thread {
    private int countDown = 5;
    private String name;
    private static Date dt = new Date( );
    private static DateFormat df = new SimpleDateFormat("HH:mm:ss:SSS");
    private volatile double d=0; // no optimization
    public SimpleThreadPr(String name, int prior) {
        this.name = name;
        setPriority(prior);
        System.out.println("\t\tMaking " + name);
    }
    public void run() {
        dt.setTime(System.currentTimeMillis( ));
        System.out.println(name+"start at "+df.format(dt)+"pr -> "+getPriority());
        for( ;countDown>0; countDown--) {

            // An expensive, interruptible 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("\t\tThe Threads with low priority started");
        for(int i = 0; i < 3; i++)
            st[i] .start();
        System.out.println("\t\tAll Threads Started");
    }

 

        Making Nick
        Making Marie
        Making George
        Making Isabelle
        Making Pierre
        Making Rose
        Making Salome
        The Threads with low priority started
Isabellestart at 21:21:18:326pr -> 1
Pierrestart at 21:21:18:326pr -> 1
Rosestart at 21:21:18:326pr -> 1
Salomestart at 21:21:18:327pr -> 1
Nickstart at 21:21:18:328pr -> 10
Mariestart at 21:21:18:328pr -> 10
        All Threads Started
Georgestart at 21:21:18:329pr -> 10
Thread George(5) priority -> 10
Thread Isabelle(5) priority -> 1
Thread Salome(5) priority -> 1
Thread Nick(5) priority -> 10
Thread Rose(5) priority -> 1
Thread Pierre(5) priority -> 1
Thread Marie(5) priority -> 10
Thread George(4) priority -> 10
Thread Isabelle(4) priority -> 1
Thread Nick(4) priority -> 10
Thread Salome(4) priority -> 1
Thread Marie(4) priority -> 10
Thread Pierre(4) priority -> 1
Thread Rose(4) priority -> 1
Thread George(3) priority -> 10
Thread Rose(3) priority -> 1
Thread Isabelle(3) priority -> 1
Thread Pierre(3) priority -> 1
Thread Nick(3) priority -> 10
Thread Marie(3) priority -> 10
Thread Salome(3) priority -> 1
Thread Rose(2) priority -> 1
Thread George(2) priority -> 10
Thread Isabelle(2) priority -> 1
Thread Salome(2) priority -> 1
Thread Pierre(2) priority -> 1
Thread Nick(2) priority -> 10
Thread Marie(2) priority -> 10
Thread Rose(1) priority -> 1
Thread Rose end
Thread George(1) priority -> 10
Thread George end
Thread Isabelle(1) priority -> 1
Thread Isabelle end
Thread Pierre(1) priority -> 1
Thread Pierre end
Thread Salome(1) priority -> 1
Thread Salome end
Thread Nick(1) priority -> 10
Thread Marie(1) priority -> 10
Thread Marie end
Thread Nick end