Turtle Graphics with Java

Bern University of Teacher Education  
HomeStart Online-EditorPrintAndroid-TurtlegraphicsJava-Online

Threads

Gemäss dem vonNeumannschen Prinzip ist ein Computer grundsätzlich eine sequentielle Maschine, d.h ein Programm wird Anweisung um Anweisung abgearbeitet. Dieses Modell genügt den heutigen Anforderungen nicht mehr. Unter den heutigen Betriebssystemen laufen mehrere Programme parallel (Multitasking). Da nur ein Prozessor zur Verfügung steht, wird dieses durch eine schnelle Umschaltung zwischen den Prozessen erreicht. Dabei werden den einzelnen Programmen während einer gewissen Zeit Ressource freigegeben (unlocked) und später wirde gesperrt (locked) .

Auch Teile eines einzelnen Java-Programms können auf mehrere Prozesse aufgeteilt werden, Man spricht dabei von Threads.

Ohne Verwendung von Threads können zwei Turtle nicht gleichzeitig laufen. Sie zeichnen abwechslungsweise je eine Treppenstufe..

Beispiel zeigen (WithoutThread.java)

Beispiel im Online-Editor bearbeiten

// WithoutThread.java

import ch.aplu.turtle.*;
import java.awt.Color;

public class WithoutThread
{
  public WithoutThread()
  {
    Turtle john = new Turtle()
    Turtle laura = new Turtle(john)
    laura.setColor(Color.red);
    laura.setPenColor(Color.red);
    laura.setPos(0160);
    laura.left(90);
    for (int i = 0; i < 8; i++)
    {
      step(john);
      step(laura);
    }  
  } 
  
  private void step(Turtle t)
  {
    t.forward(20);
    t.left(90);
    t.forward(20);
    t.right(90);
  } 

  public static void main(String[] args)
  {
    new WithoutThread();
  }
}
 


Unter Verwendung von Threads können die beiden Turtle gleichzeitig laufen. Dazu deklariert man eine (innere) Klasse JohnThread, die aus Thread abgeleitet ist und überschreibt die Methode run(). Der Thread wird in Gang gesetzt, indem man eine Instanz dieser Klasse erzeugt und die Methode start() aufruft. Dadurch führt das JRE die Methode run() in einem eigenständigen Thread aus (die Methode run() darf nie vom Anwender aufgerufen werden).

Beispiel zeigen (ThreadEx1.java)

Beispiel im Online-Editor bearbeiten

// ThreadEx1.java

import ch.aplu.turtle.*;
import java.awt.Color;

public class ThreadEx1 extends TurtleFrame
{
  public ThreadEx1()
  {
    Turtle john = new Turtle(this);
    Turtle laura = new Turtle(this);
    laura.setColor(Color.red);
    laura.setPenColor(Color.red);
    laura.setPos(0, 160);
    laura.left(90);
    draw(john);
    draw(laura);
  }

  private void draw(final Turtle t)
  {
    new Thread()
    {
      public void run()
      {
        for (int = 0; i < 8; i++)
          step(t);
      }
    }.start();
  }


  private void step(Turtle t)
  {
    t.forward(20);
    t.left(90);
    t.forward(20);
    t.right(90);
  }

  public static void main(String[] args)
  {
    new ThreadEx1();
  }
}
   


Erklärungen zum Programmcode:
Bei genauerer Betrachtung erkennt man, dass die beiden Turtles auch jetzt nicht gleichzeitig bewegt werden (es findet keine echte Parallelausführung statt). Hingegen werden die Threads auch innerhalb der Vorwärts- und Rotationsbewegung gegenseitig unterbrochen.

 

Beispiel 2:
Es ist etwas unschön, zwei Klassen mit praktisch demselben Code zu deklarieren. Man kann diese Codeduplikation vermeiden, indem man eine einzige Klasse Animator deklariert, der im Konstruktor die Turtle-Referenz übergeben wird, die bewegt werden soll.

Beispiel zeigen (ThreadEx2.java)

Beispiel im Online-Editor bearbeiten

// ThreadEx2.java

import ch.aplu.turtle.*;
import java.awt.Color;

public class ThreadEx2 extends TurtleFrame
{
  public ThreadEx2()
  {
    Turtle john = new Turtle(this);
    Turtle laura = new Turtle(thisColor.red);
    Turtle sara = new Turtle(thisColor.green);
    john.setPos(-100-100);
    laura.setPos(-20-100);
    sara.setPos(60, -100);
    laura.setPenColor(Color.red);
    sara.setPenColor(Color.green);
    draw(john);
    draw(laura);
    draw(sara);
  }

  private void draw(final Turtle t)
  {
    new Thread()
    {
      public void run()
      {
        for (int = 0; i < 5; i++)
          square(t);
      }
    }.start();
  }

  private void square(Turtle t)
  {
    for (int i = 0; i < 4; i++)
      t.forward(40).right(90);
    t.forward(40);
  }

  public static void main(String[] args)
  {
    new ThreadEx2();
  }

}
   

Erklärungen zum Programmcode:
new Animator(john).start() Zur Vereinfachung wird eine anonyme Klasse erzeugt und deren Methode start() auf gerufen

 

Beispiel 3:
Im nächsten Beispiel wird ersichtlich, dass das willkürliche Unterbrechen des Applikationsthreads, der die Turtle ein Quadrat zeichnen lässt, durch einen Mausklick, der die Turtle an eine neue Anfangsposition setzt, zu einem Durcheinander führt. (Die Callbackmethode mousePressed() wird im sogenannten Event Dispatch Thread (EDT) ausgeführt.)

Beispiel zeigen (ThreadEx3.java)

Beispiel im Online-Editor bearbeiten

// ThreadEx3.java

import ch.aplu.turtle.*;
import java.awt.Color;

public class ThreadEx3 extends TurtleFrame
{
  public ThreadEx3()
  {
    Turtle john = new Turtle(this);
    Turtle laura = new Turtle(thisColor.red);
    Turtle sara = new Turtle(thisColor.green);
    john.setPos(-135-50);
    laura.setPos(-10-50);
    sara.setPos(115, -50);
    laura.setPenColor(Color.red);
    sara.setPenColor(Color.green);
    new TurtleThread(john).start();
    new TurtleThread(laura).start();
    new TurtleThread(sara).start();
  }

  private class TurtleThread extends Thread
  {
    private Turtle turtle;

    private TurtleThread(Turtle turtle)
    {
      this.turtle = turtle;
    }

    public void run()
    {
      for (int = 0; i < 9; i++)
        segment(turtle);
    }
  }

  private void segment(Turtle t)
  {
    t.forward(140);
    t.right(160);
  }

  public static void main(String[] args)
  {
    new ThreadEx3();
  }
}
   

Erklärungen zum Programmcode:
addMouseListener(new MyMouseAdapter()) Es wird ein eigener MouseAdapter verwendet

 

Beispiel 4:
Um Ordnung in das Durcheinander zu bringen, müssen die beiden Threads, der Applikationsthread und der EDT, synchronisiert werden. Dabei wird das Monitorkonzept von Java eingesetzt: Jedes Objekt besitzt ein "Sperre" (auch Lock oder Monitor genannt). Beim Eintritt in einen mit synchronized() bezeichneten Block verlangt der Thread die Sperre. Falls diese nicht bereits an ein anderen Threads vergeben ist, wird sie ausgehändigt. Am Ende des Blocks gibt der Thread die Sperre wieder zurück. Andere Threads, die während dieser Zeit in den Block eintreten möchten, müssen auf die Sperre warten.

Beispiel zeigen (ThreadEx4.java)

Beispiel im Online-Editor bearbeiten

// ThreadEx4.java

import ch.aplu.turtle.*;
import java.awt.event.*;

public class ThreadEx4
{
  public ThreadEx4()
  {
    final Turtle joe = new Turtle();
    joe.hideTurtle();
    joe.addStatusBar(30);
    joe.setStatusText("Click to create a new turtle");
    joe.addMouseListener(new MouseAdapter()
    {
      public void mousePressed(MouseEvent e)
      {
        Turtle = new Turtle(joe);
        t.setScreenPos(e.getPoint());
        star(t);
      }
    });
  }

  private void star(final Turtle t)
  {
    new Thread()
    {
      public void run()
      {
        for (int = 0; i < 5; i++)
          t.fd(40).rt(144);
      }
    }.start();
  }

  public static void main(String[] args)
  {
    new ThreadEx4();
  }
}
   

Erklärungen zum Programmcode:

private void square()
{
   synchronized (john) .....
}

Der Applikationsthread verlangt die Sperre vor dem Zeichnen des Quadrats

public void mousePressed(MouseEvent evt)
{
   synchronized (john)....
}

Der EDT verlangt die Sperre zum Setzen des neuen Startpunkts