Difference between revisions of "CSC352: Java Threads and Synchronization Examples"
m (Thiebaut moved page CSC352: Computing Pi and Synchronization to CSC352: Java Threads and Synchronization Examples) |
(→Output) |
||
Line 304: | Line 304: | ||
<br /> | <br /> | ||
+ | =Creating an Array of Threads= | ||
+ | <br /> | ||
+ | In this example we pass the number N of iterations to the application, as well as the number of threads desired. Typical use of the application: | ||
+ | <br /> | ||
+ | javac DT/SynchronizedThreadExample4.java | ||
+ | java DT.SynchronizedThreadExample4 100000000 10 | ||
+ | <br /> | ||
+ | ==Source Code== | ||
+ | <br /> | ||
+ | <source lang="java"> | ||
+ | /* | ||
+ | * SynchronizedThreadExample4.java | ||
+ | * D. Thiebaut | ||
+ | */ | ||
+ | package DT; | ||
+ | |||
+ | import java.util.ArrayList; | ||
+ | |||
+ | public class SynchronizedThreadExample4 { | ||
+ | |||
+ | int sum; | ||
+ | Object lock; | ||
+ | int noThreads = 2; | ||
+ | class PiThread extends Thread { | ||
+ | private int N; // the total number of samples/iterations | ||
+ | |||
+ | public PiThread( int Id, int N ) { | ||
+ | super( "Thread-"+Id ); // give a name to the thread | ||
+ | this.N = N; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println( getName() + " started!" ); | ||
+ | for ( int i=0; i<N; i++ ) | ||
+ | synchronized( lock ) { | ||
+ | sum ++; | ||
+ | } | ||
+ | System.out.println( getName() + " done!" ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | SynchronizedThreadExample4() { | ||
+ | sum = 0; | ||
+ | lock = new Object(); | ||
+ | } | ||
+ | |||
+ | public void process( int N, int noThreads ) { | ||
+ | |||
+ | ArrayList<PiThread> threads = new ArrayList<PiThread>(); | ||
+ | |||
+ | //--- create and start threads --- | ||
+ | for ( int t=0; t<noThreads; t++ ) { | ||
+ | PiThread T = new PiThread( t, N/noThreads ); | ||
+ | threads.add( T ); | ||
+ | T.start(); | ||
+ | } | ||
+ | |||
+ | //--- wait till they finish --- | ||
+ | for ( PiThread T : threads ) | ||
+ | try { | ||
+ | T.join(); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | long startTime = System.currentTimeMillis(); | ||
+ | |||
+ | int N = 10000000; | ||
+ | int noThreads = 8; | ||
+ | |||
+ | if ( args.length >= 1 ) N = Integer.parseInt( args[0] ); | ||
+ | if ( args.length >=2 ) noThreads = Integer.parseInt( args[1] ); | ||
+ | |||
+ | SynchronizedThreadExample4 S4 = new SynchronizedThreadExample4(); | ||
+ | S4.process( N, noThreads ); | ||
+ | |||
+ | //--- output result in columnar fashion for easy copy/paste into spreadsheet --- | ||
+ | long endTime = System.currentTimeMillis(); | ||
+ | System.out.println( S4.sum + " " + noThreads + " " + (endTime-startTime) ); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | </source> | ||
<br /> | <br /> | ||
+ | ==Output== | ||
+ | '''java DT.SynchronizedThreadExample4 100000000 10''' | ||
+ | Thread-1 started! | ||
+ | Thread-2 started! | ||
+ | Thread-3 started! | ||
+ | Thread-4 started! | ||
+ | Thread-5 started! | ||
+ | Thread-6 started! | ||
+ | Thread-7 started! | ||
+ | Thread-8 started! | ||
+ | Thread-9 started! | ||
+ | Thread-1 done! | ||
+ | Thread-6 done! | ||
+ | Thread-4 done! | ||
+ | Thread-9 done! | ||
+ | Thread-2 done! | ||
+ | Thread-8 done! | ||
+ | Thread-0 done! | ||
+ | Thread-5 done! | ||
+ | Thread-3 done! | ||
+ | Thread-7 done! | ||
+ | 100000000 10 5204 | ||
+ | |||
<br /> | <br /> | ||
<br /> | <br /> |
Revision as of 07:39, 11 September 2013
--D. Thiebaut (talk) 21:12, 4 September 2013 (EDT)
Contents
A Badly Written (and Flawed) Multithreaded Computation of Pi
/*
* UnsynchronizedThreadExample.java
* D. Thiebaut
* Undocumented code that computes Pi with 2 threads, but is terribly
* flawed in the way it updates the global sum...
*/
package DT;
public class UnsynchronizedThreadExample {
static int sum = 0;
class PiThreadBad extends Thread {
private int N; // the total number of samples/iterations
public PiThreadBad( int Id, int N ) {
super( "Thread-"+Id ); // give a name to the thread
this.N = N;
}
@Override
public void run() {
for ( int i=0; i<N; i++ )
sum ++;
}
}
public void process( int N ) {
long startTime = System.currentTimeMillis();
PiThreadBad t1 = new PiThreadBad( 0, N );
PiThreadBad t2 = new PiThreadBad( 1, N );
//--- start two threads ---
t1.start();
t2.start();
//--- wait till they finish ---
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "sum = " + sum );
System.out.println( "Execution time: " + (System.currentTimeMillis()-startTime) + " ms" );
}
public static void main(String[] args) {
int N = 100000000;
UnsynchronizedThreadExample U = new UnsynchronizedThreadExample();
U.process( N );
}
}
Output
sum = 180612836 Execution time: 19 ms
Note that the sum should really be 200000000, as both threads increment sum 100000000 times. The result is certainly incorrect.
Note also that the execution time is quite fast: 19 ms.
A Synchronized Version of the Same Program
We decide to put the statement that increments the variable sum into a function, and ask Java to synchronize around the function, i.e. make sure than only one thread at a time runs through this function. In other word, the synchronized function becomes atomic for threads.
package DT;
public class SynchronizedThreadExample {
int sum = 0;
Integer lock =0;
//SynchronizedThreadExample() {
// sum = 0;
// lock = new Integer( 0 );
//}
class PiThreadGood extends Thread {
private int N; // the total number of samples/iterations
public PiThreadGood( int Id, int N ) {
super( "Thread-"+Id ); // give a name to the thread
this.N = N;
}
@Override
public void run() {
for ( int i=0; i<N; i++ )
synchronized( lock ) {
sum++;
}
}
}
public void process( int N ) {
long startTime = System.currentTimeMillis();
PiThreadGood t1 = new PiThreadGood( 0, N );
PiThreadGood t2 = new PiThreadGood( 1, N );
//--- start two threads ---
t1.start();
t2.start();
//--- wait till they finish ---
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "sum = " + sum );
System.out.println( "Execution time: " + (System.currentTimeMillis()-startTime) + " ms" );
}
public static void main(String[] args) {
int N = 100000000;
SynchronizedThreadExample U = new SynchronizedThreadExample();
U.process( N );
}
}
Output
sum = 200000000 Execution time: 8448 ms
Note that the result is now correct. However the execution time is 400 longer!
A second way of synchronizing the threaded computation
This time, instead of creating a synchronized method (by the way, the synchronized method should not be one of the thread's method, but a method outside the inherited thread class), we synchronize on an object global to the threads and the main program. This object cannot be a simple type (such as int), but a real object (e.g. Integer).
package DT;
public class SynchronizedThreadExample2 {
static int sum = 0;
class PiThreadGood extends Thread {
private int N; // the total number of samples/iterations
public PiThreadGood( int Id, int N ) {
super( "Thread-"+Id ); // give a name to the thread
this.N = N;
}
@Override
public void run() {
for ( int i=0; i<N; i++ )
incrementSum();
}
}
private synchronized void incrementSum() {
sum++;
}
public void process( int N ) {
long startTime = System.currentTimeMillis();
PiThreadGood t1 = new PiThreadGood( 0, N );
PiThreadGood t2 = new PiThreadGood( 1, N );
//--- start two threads ---
t1.start();
t2.start();
//--- wait till they finish ---
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "sum = " + sum );
System.out.println( "Execution time: " + (System.currentTimeMillis()-startTime) + " ms" );
}
public static void main(String[] args) {
int N = 100000000;
SynchronizedThreadExample2 U = new SynchronizedThreadExample2();
U.process( N );
}
}
Output
sum = 200000000 Execution time: 8620 ms
Similar behavior as the first version. The synchronization code definitely add a serious overhead to the computation. Sometimes it is a necessary solution for a problem. In other cases, such as in the computation of Pi, we can find an approach that is safe but does not require synchronization.
A Third Synchronized Version of the Same Program
Similar to the first synchronized solution, but this time using Objects instead of Integers.
package DT;
public class SynchronizedThreadExample3 {
int sum = 0;
Object lock;
SynchronizedThreadExample3() {
sum = 0;
lock = new Object();
}
class PiThreadGood extends Thread {
private int N; // the total number of samples/iterations
public PiThreadGood( int Id, int N ) {
super( "Thread-"+Id ); // give a name to the thread
this.N = N;
}
@Override
public void run() {
for ( int i=0; i<N; i++ )
synchronized( lock ) {
sum++;
}
}
}
public void process( int N ) {
long startTime = System.currentTimeMillis();
PiThreadGood t1 = new PiThreadGood( 0, N );
PiThreadGood t2 = new PiThreadGood( 1, N );
//--- start two threads ---
t1.start();
t2.start();
//--- wait till they finish ---
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "sum = " + sum );
System.out.println( "Execution time: " + (System.currentTimeMillis()-startTime) + " ms" );
}
public static void main(String[] args) {
int N = 100000000;
SynchronizedThreadExample U = new SynchronizedThreadExample();
U.process( N );
}
}
Output
sum = 200000000 Execution time: 6666 ms
Note that the result is again correct. However the execution time is still very long.
Creating an Array of Threads
In this example we pass the number N of iterations to the application, as well as the number of threads desired. Typical use of the application:
javac DT/SynchronizedThreadExample4.java java DT.SynchronizedThreadExample4 100000000 10
Source Code
/*
* SynchronizedThreadExample4.java
* D. Thiebaut
*/
package DT;
import java.util.ArrayList;
public class SynchronizedThreadExample4 {
int sum;
Object lock;
int noThreads = 2;
class PiThread extends Thread {
private int N; // the total number of samples/iterations
public PiThread( int Id, int N ) {
super( "Thread-"+Id ); // give a name to the thread
this.N = N;
}
@Override
public void run() {
System.out.println( getName() + " started!" );
for ( int i=0; i<N; i++ )
synchronized( lock ) {
sum ++;
}
System.out.println( getName() + " done!" );
}
}
SynchronizedThreadExample4() {
sum = 0;
lock = new Object();
}
public void process( int N, int noThreads ) {
ArrayList<PiThread> threads = new ArrayList<PiThread>();
//--- create and start threads ---
for ( int t=0; t<noThreads; t++ ) {
PiThread T = new PiThread( t, N/noThreads );
threads.add( T );
T.start();
}
//--- wait till they finish ---
for ( PiThread T : threads )
try {
T.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int N = 10000000;
int noThreads = 8;
if ( args.length >= 1 ) N = Integer.parseInt( args[0] );
if ( args.length >=2 ) noThreads = Integer.parseInt( args[1] );
SynchronizedThreadExample4 S4 = new SynchronizedThreadExample4();
S4.process( N, noThreads );
//--- output result in columnar fashion for easy copy/paste into spreadsheet ---
long endTime = System.currentTimeMillis();
System.out.println( S4.sum + " " + noThreads + " " + (endTime-startTime) );
}
}
Output
java DT.SynchronizedThreadExample4 100000000 10 Thread-1 started! Thread-2 started! Thread-3 started! Thread-4 started! Thread-5 started! Thread-6 started! Thread-7 started! Thread-8 started! Thread-9 started! Thread-1 done! Thread-6 done! Thread-4 done! Thread-9 done! Thread-2 done! Thread-8 done! Thread-0 done! Thread-5 done! Thread-3 done! Thread-7 done! 100000000 10 5204