In your previous study, most of the applications, that you have implemented, are sequential programs -- programs with a single flow of control. In CMSC417, we need to develop applications that can handle many "tasks" concurrently. As an example, a server program sends to many clients and receives from many clients at the same time. In a sequential program, server program sends to one client, receives from this client, and then moves to another client to repeat the same sequence. A better solution is to use threads. A thread is a single sequential flow of control within a program. So to solve the server problem, server program initiates two threads for every client: one for sending and anther for receiving.
Example 1
Example 1 is available at ExNotSync.java:
class ExNotSync {
public static void main (String argv[]){
new Counter();
}
}
// This class has a counter. It creates four threads, where the
counter is shared
// among them.
// Each thread repeats the following sequence for 25000 times:
// - Reads the counter value into a variable
temp.
// - Increments temp by 1.
// - Stores temp into counter.
// After incrementing the counter for 100000, the value of the
counter should
// be 100000. But after running the application, the value of the
counter is
// less than 100000. The reason for that is the lack of synchronization
// inside the loop.
// (Note: I choose the loop iterations number to be 25000 iterations
to allow
// interleaving of the threads. For fast computers, you may
want to increase
// that number.)
class Counter {
static int counter = 0;
Counter(){
CounterThread th1 =
new CounterThread("th1");
CounterThread th2 =
new CounterThread("th2");
CounterThread th3 =
new CounterThread("th3");
CounterThread th4 =
new CounterThread("th4");
th1.start();
th2.start();
th3.start();
th4.start();
System.out.println("Threads
running.");
}
class CounterThread extends Thread {
String name;
CounterThread(String
name){
this.name = name;
}
public void run(){
for (int i=0; i < 25000; i++){
int temp = counter;
yield();
temp = temp + 1;
yield();
counter = temp;
yield();
}
System.out.println(name + " counter = " + counter);
}
}
}
This program is compiled using command "javac ExNotSync.java", and executed using command "java ExNotSync".
In example 1, the main class is ExNotSync. It has method main, which is called when executing command "java ExNotSync". Example 1 has a class Counter, which defines an integer counter initiated to zero. Constructor of class Counter creates 4 objects (an instance of a class) of type CounterThread. Class CounterThread extends (or inherits) class Thread. The sequential control sequence that CounterThread executes is the body of method "public void run()". To create a object of a CounterThread (or any class that extends Thread), you write "<obj> = new CounterThread(<name>)". To let an object of CounterThread start executing, you call "<obj>.start()". Do not call "<obj>.run()".
In this example, each thread increments a common counter for 25000 times. The value of the counter should be equal to 4*25000 at the end of the execution, but this does not happen. The following figure is a snapshot of the execution of threads th1 and th2 during time interval [t,t']:
In this figure, two increments to variable counter took place, but they had the effect of just one increment. The reason is that every thread runs independently from all other threads, though they handle a shared counter. So, there has to be a method to synchronize access to the shared counter, so that at most one increment loop takes place at a time. The following figure is a snapshot of synchronized access to counter during interval [t,t'] during execution:
Example 2
In this example, we will fix the synchronization problem in the previous
example.
Example 2 is available at ExSync.java:
class ExSync {
public static void main (String argv[]) {
new Counter();
}
}
// This class has a counter. It creates four threads, where the
counter is shared
// among them.
// Each thread repeats the following sequence for 25000 times:
// - Reads the counter value into a variable
temp.
// - Increments temp by 1.
// - Stores temp into counter.
// After incrementing the counter for 100000, the value of the
counter is
// be 100000. The loop body is synchronized to allow at most one
thread to
// execute the loop body at a time.
class Counter {
static int counter = 0;
static Object lock = new Object();
Counter(){
CounterThread th1 =
new CounterThread("th1");
CounterThread th2 =
new CounterThread("th2");
CounterThread th3 =
new CounterThread("th3");
CounterThread th4 =
new CounterThread("th4");
th1.start();
th2.start();
th3.start();
th4.start();
System.out.println("Threads
running.");
}
class CounterThread extends Thread {
String name;
CounterThread(String
name){
this.name = name;
}
public void run(){
for (int i=0; i < 25000; i++){
synchronized(lock){
int temp = counter;
yield();
temp = temp + 1;
yield();
counter = temp;
yield();
}
}
System.out.println(name + " counter = " + counter);
}
}
}
This program is compiled using command "javac ExSync.java", and executed using command "java ExSync".
It is the same as example 1, except:
This is not the only way to solve the problem (what are the other ways?).