Studyon Minte9.com
Head First (Java)




Threads



	// separated processes
	// bottom of the stack
	// run()
	// sleep() - order execution workaround
	// threads - randomly executed
	// concurency - problem, 2 threads, one method
	// Synchronized - solution for concurency


* Separate processes

	Unless you have multiple processors on your computer, each new Java thread is not actually a 
	separate process running on the OS. But it almost feel as though it is.
	

* Bottom of the stack (run method)

	Every Java application starts up a main thread and puts the main() method on the bottom of the stack. 
	A new thread creats a separate call stack and puts the run() method on the bottom of the stack.


* Runnable

	To make a job for your thread, implement the Runnable interface.

	%java

		package testexample;
		import java.io.*;
		import java.net.*;

		public class Testexample{
		    
		    public static void main(String[] args) {
			
			Runnable threadJob = new MyRunnable();
			Thread myThread = new Thread(threadJob);
			
			myThread.start();
			
			System.out.println("Back in main");
		    }
		}

		class MyRunnable implements Runnable {
		    
		    @Override
		    public void run() {
			System.out.println("Thread run");
			go();
		    }
		    public void go() {
			System.out.println("Thread go");
			go2();
		    }
		    public void go2() {
			System.out.println("Thread go2");
		    }
		}

		/* Output:
			Back in main
			Thread run
			Thread go
			Thread go2

		   Another output might be:
			Thread go2
			Back in main
			Thread run
			Thread go
		*/


* Sleep
	
	Thread scheduler makes all the decisions about who runs and who doesn't. Do not base your 
	program's correctness on the scheduler working in a particular way. The scheduler implementations 
	are different for differnt JVM's.

	One of the best ways to help your threads take turns is to put them to sleep periodically.

	%java

		package testexample;
		import java.io.*;
		import java.net.*;

		public class Testexample{
		    
		    public static void main(String[] args) {
			
			Runnable threadJob = new MyRunnable();
			Thread myThread = new Thread(threadJob);
			
			myThread.start();
			
			System.out.println("Back in main");
		    }
		}

		class MyRunnable implements Runnable {
		    
		    @Override
		    public void run() {

			System.out.println("Thread run");
			go();
		    }
		    public void go() {
			
			// Thread sleep
			try{
			    Thread.sleep(2000);
			} catch(InterruptedException e) {
			    e.printStackTrace();
			}
			
			// The main thread will became the current running tread
			// and prints "Back in main"

			System.out.println("Thread go");
			go2();
		    }
		    public void go2() {
			System.out.println("Thread go2");
		    }
		}


* Multiple threads

	Threads have names (default or given by you)

	package testexample;
	import java.io.*;
	import java.net.*;

	public class Testexample implements Runnable {
	    
	    public static void main(String[] args) {
		Testexample runner = new Testexample();
		Thread alpha = new Thread(runner);
		Thread beta = new Thread(runner);
		alpha.setName("Alpha thread");
		beta.setName("Beta thread");
		alpha.start();
		beta.start();
	    }
	    
	    @Override
	    public void run() {
		for (int i=0; i<125; i++) {
		    System.out.println(Thread.currentThread().getName() + " is running");
		}
	    }
	}

	/* 
	 * How often will they switch. We don't know!
	 */


* Concurency problem

	Potential deadly scenario: two or more threads have access to a single objct's data. 
	We need a lock for account access.

	% java

		package testexample;
		import java.io.*;
		import java.net.*;

		public class Testexample implements Runnable {
		    
		    private BankAccount account = new BankAccount();
		    
		    public static void main(String[] args) {
			
			// runnable
			Testexample runner = new Testexample();
			
			// threads
			Thread alpha = new Thread(runner);
			Thread beta = new Thread(runner);
			alpha.setName("Alpha");
			beta.setName("Beta");
			alpha.start();
			beta.start();
		    }
		    
		    @Override
		    public void run() {
			for (int i=0; i<2; i++) {
			    System.out.println("Account balance: " + account.getBalance());
			    makeWithdraw(10);
			}
		    }
		    
		    private void makeWithdraw(int amount) {
			if (account.getBalance() >= amount) {
			    try {
				System.out.println(Thread.currentThread().getName() + " is going to sleep");
				Thread.sleep(500); 
			    } catch (InterruptedException e) { e.printStackTrace(); }
			    account.withdraw(amount);
			} else {
			}
		    }
		}

		class BankAccount {
		    
		    private int balance = 20;
		    
		    public int getBalance() {
			return balance;
		    }
		    
		    public void withdraw(int amount) {
			balance = balance - amount;
			System.out.println(Thread.currentThread().getName() + " withdraw 10");
		    }
		}

		/* Outputs:

			Account balance: 20
			Alpha is going to sleep
			Account balance: 20
			Beta is going to sleep
			Alpha withdraw 10
			Account balance: 10
			Alpha is going to sleep
			Beta withdraw 10

			Account balance: 0
			Alpha withdraw 10 (error !!!)
		*/


* Synchronized

	We use "syncronized" keyword to modify a method so that only one thread at a time can access it.

	Every object has a lock. Most of the time, the lock is unlocked, and you can imagine a virtual key 
	sitting with it.

	% java

		package testexample;
		import java.io.*;
		import java.net.*;

		public class Testexample implements Runnable {
		    
		    private BankAccount account = new BankAccount();
		    
		    public static void main(String[] args) {
			
			// runnable
			Testexample runner = new Testexample();
			
			// threads
			Thread alpha = new Thread(runner);
			Thread beta = new Thread(runner);
			alpha.setName("Alpha");
			beta.setName("Beta");
			alpha.start();
			beta.start();
		    }
		    
		    @Override
		    public void run() {
			for (int i=0; i<2; i++) {
			    System.out.println("Account balance: " + account.getBalance());
			    makeWithdraw(10);
			}
		    }
		    
		    /* --- LOOK HERE --- */

		    private synchronized void makeWithdraw(int amount) {
			if (account.getBalance() >= amount) {
			    try {
				System.out.println(Thread.currentThread().getName() + " is going to sleep");
				Thread.sleep(500); 
			    } catch (InterruptedException e) { e.printStackTrace(); }
			    account.withdraw(amount);
			} else {
			}
		    }
		}

		class BankAccount {
		    
		    private int balance = 20;
		    
		    public int getBalance() {
			return balance;
		    }
		    
		    public void withdraw(int amount) {
			balance = balance - amount;
			System.out.println(Thread.currentThread().getName() + " withdraw 10");
		    }
		}

		/* Outputs: (now is correct) /* --- LOOK HERE -- */

			Account balance: 20
			Account balance: 20
			Alpha is going to sleep
			Alpha withdraw 10
			Account balance: 10
			Alpha is going to sleep
			Alpha withdraw 10
			Account balance: 0
		*/

	It's not good idea to synchronize everything. (performance overhead)