הלינקייה: מגזין חודשי למפתחים

רוצה לשמוע על כל האירועים, המדריכים, הקורסים והמאמרים שנכתבו החודש ?
הלינקייה הינו מגזין חופשי בעברית שמשאיר אותך בעניינים.
בלי ספאם. בלי שטויות. פעם בחודש אצלך בתיבה.

Threads

A Thread is a small unit of execution capable of holding just the program counter, which is the place in the code the program is now at. When we talk about multi threaded programming in Java, we refer to the paradigm of having more than one execution context running at the same time. All the threads in a java program share the same memory heap.
Let's clear this out a bit - if our program was a production line at the factory, having multiple threads means we can now operate two, three or even five production lines running concurrently with one another. This can have a great impact on performance, allowing us to do some work "in the background", while we wait for some external event to happen, or just split our task into smaller execution units.

In java, all of IO operations are blocking. This means if we send a file on the network, we must wait for the file to actually get sent in order to move on and do some other things. Having multiple threads allows us to both send the file, and keep doing other tasks at the same time.

The basic pattern for starting a new thread of execution involves creating a new class implementing the Runnable interface. This is a common pattern for java development - every time we need to pass a function, we just wrap it in a class. For a thread, the functionality we need to decide is the "main" function of the thread, which is called run() in the Runnable interface. In the run() method we get to write whatever code we want to get done in that other thread.

package actions;
 
public class HelloAction implements Runnable {

	public void run() {
                System.out.println("Hello World");			
	}
}

When our class is ready, we create a new Thread object, passing our Runnable class in the constructor.
The Thread is a native java class - it represents a thread of execution and has all the methods related to running the thread.
When we have the thread object at hand, we call its start() method to actually create the new thread. Note the name difference between our given run() method and the native start().

Here is another example for using basic threads in Java:

package actions;
 
public class HelloAction implements Runnable {
	String name;
 
	public HelloAction(String name) {
		this.name = name;
	}
 
	public void run() {
		for ( int i=0 ; i < 1000; i++ ) {
			System.out.println("Hello, " + this.name);			
		}
	}
}
package actions;
 
public class SumAction implements Runnable {
	int start;
	int end;
	int sum;
 
	public SumAction(int start, int end) {
		this.start = start;
		this.end = end;
		this.sum = 0;
	}
 
	public void run() {		
		for ( int i=this.start; i < this.end; ++i ) {
			this.sum += i;
		}
	}
 
	public int getSum() {
		return this.sum;
	}
}
package mt;
import actions.HelloAction;
 
public class Example1 {
	public static void main(String [] args) {
		HelloAction h1 = new HelloAction("Bill");
		HelloAction h2 = new HelloAction("Hillary");
 
		Thread t1 = new Thread(h1);
		Thread t2 = new Thread(h2);
 
		/*
		h1.run();
		h2.run();
		*/
 
		t1.start();
		t2.start();
	}
}
package mt;
 
import actions.SumAction;
 
public class Example2 {
	public static void main(String [] args) throws InterruptedException {
		SumAction s1 = new SumAction(1, 10000);
		SumAction s2 = new SumAction(10000, 20000);
 
		Thread t1 = new Thread(s1);
		Thread t2 = new Thread(s2);
 
		t1.start();
		t2.start();
 
		t1.join();
		t2.join();
 
		int total = s1.getSum() + s2.getSum();
		System.out.println("Total sum = " + total);
	}
}
package app;
 
public class Racer implements Runnable {
	private String name;
	private long sum;
	private FinishLine line;
	boolean isAlive;
 
	public Racer(String name, FinishLine l) {
		this.name = name;
		this.line = l;
		this.isAlive = true;
	}
 
	public String getName() {
		return this.name;
	}
 
	public String toString() {
		return this.name;
	}
 
	public long getSum() {
		return this.sum;
	}
 
	public void run() {
		this.sum = 0;
 
		for ( long i=0; i < 50000000; ++i ) {
			this.sum += i;
 
			if ( i % 1000 == 0 ) {
				Thread.yield();
			}
 
			if ( ! isAlive ) {
				break;
			}
		}
 
		if ( isAlive ) {
			this.line.finish(this);	
		}		
	}
 
	public synchronized void getEaten() {
		System.out.println(this + ": Oh no the devils got me. I only got till " + this.sum);
		this.isAlive = false;
	}
}
package app;
 
import java.util.LinkedList;
import java.util.List;
 
public class TheRace {
	public static void main(String [] args) throws InterruptedException {
		List<Thread> lt = new LinkedList<Thread>();
		List<Racer>  lr = new LinkedList<Racer>();
 
		FinishLine line = new FinishLine();
 
		for ( int i=0; i < 10; ++i ) {
			Racer r = new Racer("Shrek_" + i, line);
			Thread t = new Thread(r);
			lt.add(t);
			lr.add(r);
			t.start();
		}
 
		WildAnimal lucifer = new WildAnimal(lr);
		Thread w = new Thread(lucifer);
		w.start();
 
		for(Thread t : lt ) {
			t.join();
		}
 
		lucifer.getBackToHell();
		w.join();
 
		Racer winner = line.whoWasFirst();
		System.out.println("And the winner is: " + winner);
	}
}
package app;
 
public class FinishLine {
	Racer first;
 
	public FinishLine() {
		this.first = null;
	}
 
	public synchronized void finish(Racer r) {
		// in the end we have the sum of all numbers
		System.out.println(r + ": Bingo ! ( " + r.getSum() + ")");
 
		if ( this.first == null ) {
			this.first = r;
		}
	}
 
	public Racer whoWasFirst() {
		return this.first;
	}
}
package app;
 
import java.util.List;
 
public class WildAnimal implements Runnable {
	List<Racer> racers;
	boolean raceIsOn;
 
	public WildAnimal(List<Racer> r) {
		this.racers = r;
		this.raceIsOn = true;
	}
 
	public void getBackToHell() {
		this.raceIsOn = false;
	}
 
	public void run() {
		while ( raceIsOn ) {
			int victim = (int) (Math.random() * racers.size());
			racers.get(victim).getEaten();
			try {
				Thread.sleep(800);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}			
		}
	}
}

package t;
 
import junit.framework.Assert;
import app.FinishLine;
import app.Racer;
 
public class TestFinishLine extends FinishLine {
 
	Racer expected;
	boolean finishCalled;
 
	public void setExpected(Racer r) {
		this.expected = r;
	}
 
	public void finish(Racer r) {
		Assert.assertEquals(r, this.expected);
		this.finishCalled = true;
	}
 
}
 
package t;
import app.Racer;
 
import junit.framework.Assert;
import junit.framework.TestCase;
 
public class TestRacer extends TestCase {
	public static void testSum() {
		TestFinishLine l = new TestFinishLine();
 
		Racer r = new Racer("Ted", l);
		l.setExpected(r);
 
		r.run();
		Assert.assertEquals(12499997500000L, r.getSum());
		Assert.assertEquals(true, l.finishCalled);
	}
}
 
course: