618 lines
11 KiB
Plaintext
618 lines
11 KiB
Plaintext
Application
|
||
Programming
|
||
Hend Alkittawi
|
||
|
||
Threads & Concurrency
|
||
Introduction to Java Threads and
|
||
Synchronization
|
||
|
||
INTRODUCTION
|
||
-
|
||
|
||
All modern operating systems support concurrency, via
|
||
processes and threads.
|
||
|
||
-
|
||
|
||
A process is an instance of a program running in a computer
|
||
-
|
||
|
||
example: if you start a java program, the OS spawns a new
|
||
process, which runs in parallel to other programs.
|
||
|
||
-
|
||
|
||
A thread is a program unit that is executed concurrently with
|
||
other parts of the program.
|
||
|
||
-
|
||
|
||
One or more threads run in the context of the process.
|
||
|
||
-
|
||
|
||
Multiple threads can collaborate and work efficiently within a
|
||
single program.
|
||
|
||
THREADS
|
||
-
|
||
|
||
Multi-threaded applications
|
||
have multiple threads within
|
||
a single process
|
||
-
|
||
|
||
each thread have its own
|
||
program counter, stack and
|
||
set of registers,
|
||
|
||
-
|
||
|
||
All threads
|
||
|
||
share common
|
||
|
||
code, data, and certain
|
||
structures such as open
|
||
files.
|
||
|
||
THREADS
|
||
-
|
||
|
||
Threads are very useful in modern programming whenever a
|
||
process has multiple tasks to perform independently of the
|
||
others.
|
||
|
||
-
|
||
|
||
This is particularly true when one of the tasks may block, and
|
||
it is desired to allow the other tasks to proceed without
|
||
blocking.
|
||
-
|
||
|
||
For example in a word processor, a background thread may check
|
||
spelling and grammar while a foreground thread processes user
|
||
input ( keystrokes ), while yet a third thread loads images
|
||
from the hard drive, and a fourth does periodic automatic
|
||
backups of the file being edited.
|
||
|
||
THREADS
|
||
-
|
||
|
||
A multi-threaded application running on a single-core chip
|
||
would have to interleave the threads
|
||
|
||
-
|
||
|
||
A multi-threaded application running on a multi-core chip, the
|
||
threads could be spread across the available cores, allowing
|
||
true parallel processing
|
||
|
||
THREADS
|
||
-
|
||
|
||
The thread scheduler gives no guarantee about the order in
|
||
which threads are executed.
|
||
|
||
-
|
||
|
||
Each thread runs for a short amount of time, called a time
|
||
slice. Then the schedule activates another thread. However,
|
||
there will always be slight variations in running times. Thus,
|
||
you should expect that the order in which each thread gains
|
||
controls is somewhat random.
|
||
|
||
-
|
||
|
||
It is important to observe that the order and the timing of
|
||
operations performed by the threads are controlled by the
|
||
runtime system, and cannot be controlled by the programmer.
|
||
|
||
JAVA THREADS
|
||
-
|
||
|
||
The JVM executes each thread for a short amount of time and
|
||
then switches to another thread.
|
||
|
||
-
|
||
|
||
In a multithreaded environment, threads can be: created,
|
||
scheduled to run, paused, resumed, and terminated.
|
||
|
||
-
|
||
|
||
In Java, we can create threads within that process two
|
||
different ways
|
||
-
|
||
|
||
Create a new class of type Thread
|
||
-
|
||
|
||
-
|
||
|
||
java.lang.Thread
|
||
|
||
Create a new class that implements the Runnable interface
|
||
-
|
||
|
||
java.lang.Runnable
|
||
|
||
-
|
||
|
||
the Runnable interface has a single method called run().
|
||
|
||
JAVA THREADS
|
||
To create threads by creating a new class of type Thread
|
||
1.
|
||
|
||
Create a class that extends the Thread class.
|
||
|
||
2.
|
||
|
||
Override the run() method by placing the task code into
|
||
the run() method of your class.
|
||
|
||
3.
|
||
|
||
Create an object of the subclass
|
||
|
||
4.
|
||
|
||
Call the start method to start the thread
|
||
public class MyThread extends Thread {
|
||
@Override
|
||
public void run() {
|
||
// your code here!
|
||
}
|
||
public static void main( String[] args ){
|
||
MyThread thread = new MyThread();
|
||
thread.start();
|
||
}
|
||
}
|
||
|
||
main
|
||
|
||
-
|
||
|
||
thread
|
||
|
||
JAVA THREADS
|
||
To create threads by creating a new class that implements the
|
||
Runnable interface:
|
||
1.
|
||
|
||
Create a class that implements the Runnable interface.
|
||
|
||
2.
|
||
|
||
Place the task code into the run() method of your class.
|
||
|
||
3.
|
||
|
||
Create an object of the subclass
|
||
|
||
4.
|
||
|
||
Construct a thread object from the Runnable object.
|
||
|
||
5.
|
||
|
||
Call the start method to start the thread
|
||
public class MyRunnable implements Runnable {
|
||
public void run() {
|
||
// your code here!
|
||
}
|
||
public static void main( String[] args ){
|
||
Runnable runnable = new MyRunnable();
|
||
Thread thread = new Thread(runnable);
|
||
thread.start();
|
||
}
|
||
}
|
||
|
||
main
|
||
|
||
-
|
||
|
||
thread
|
||
|
||
JAVA THREADS
|
||
-
|
||
|
||
Main Thread
|
||
public class MainThreadDemo {
|
||
public static void main(String[] args) {
|
||
Thread t = Thread.currentThread();
|
||
System.out.println("Current thread: " + t);
|
||
t.setName("My Thread"); // set the thread name
|
||
System.out.println("After name change: " + t);
|
||
The sleep() method
|
||
try {
|
||
puts the current
|
||
for(int i = 5; i > 0; i--) {
|
||
thread to sleep for a
|
||
System.out.println(i);
|
||
given number of
|
||
Thread.sleep(500);
|
||
milliseconds
|
||
}
|
||
}
|
||
catch(InterruptedException e) {
|
||
}
|
||
Current thread: Thread[main,5,main]
|
||
After name change: Thread[My Thread,5,main]
|
||
}
|
||
5
|
||
}
|
||
4
|
||
3
|
||
2
|
||
1
|
||
|
||
Creating a thread by extending the Thread class
|
||
public class MyThreadDemo {
|
||
public static void main(String[] args) {
|
||
MyThread nt = new MyThread();
|
||
nt.start();
|
||
try {
|
||
for(int i = 5; i > 0; i--) {
|
||
System.out.printf("%-15s: %d\n", "Main Thread", i);
|
||
Thread.sleep(1000);
|
||
}
|
||
}
|
||
catch(InterruptedException e) {
|
||
}
|
||
System.out.println("Exiting Main Thread ...");
|
||
}
|
||
}
|
||
|
||
main
|
||
|
||
public class MyThread extends Thread{
|
||
public MyThread() {
|
||
super("Demo Thread");
|
||
System.out.println("Child Thread: " + this);
|
||
}
|
||
// This is the entry point for the second thread.
|
||
@Override
|
||
public void run() {
|
||
try {
|
||
for(int i = 5; i > 0; i--) {
|
||
System.out.printf("%-15s: %d\n", "Child Thread", i);
|
||
Thread.sleep(500);
|
||
}
|
||
}
|
||
catch(InterruptedException e) {
|
||
}
|
||
System.out.println("Exiting Child Thread ...");
|
||
}
|
||
}
|
||
|
||
thread
|
||
|
||
Child Thread: Thread[Demo Thread,5,main]
|
||
Main Thread: 5
|
||
Child Thread: 5
|
||
Child Thread: 4
|
||
Main Thread: 4
|
||
Child Thread: 3
|
||
Child Thread: 2
|
||
Main Thread: 3
|
||
Child Thread: 1
|
||
Exiting Child Thread ...
|
||
Main Thread: 2
|
||
Main Thread: 1
|
||
Exiting Main Thread ...
|
||
|
||
Creating a thread by implementing the Runnable interface
|
||
|
||
// This is the entry point for the second thread.
|
||
@Override
|
||
public void run() {
|
||
try {
|
||
for(int i = 5; i > 0; i--) {
|
||
System.out.printf("%-15s: %d\n", "Child Thread", i);
|
||
Thread.sleep(500);
|
||
}
|
||
}
|
||
catch(InterruptedException e) {
|
||
}
|
||
System.out.println("Exiting Child Thread ...");
|
||
}
|
||
}
|
||
|
||
public class MyRunnableDemo {
|
||
public static void main(String[] args) {
|
||
MyRunnable runnable = new MyRunnable();
|
||
Thread thread = new Thread(runnable);
|
||
thread.start();
|
||
try {
|
||
for(int i = 5; i > 0; i--) {
|
||
System.out.printf("%-15s: %d\n", "Main Thread", i);
|
||
Thread.sleep(1000);
|
||
}
|
||
}
|
||
catch(InterruptedException e) {
|
||
}
|
||
System.out.println("Exiting Main Thread ...");
|
||
}
|
||
}
|
||
|
||
main
|
||
|
||
public class MyRunnable implements Runnable{
|
||
|
||
thread
|
||
|
||
Main Thread
|
||
: 5
|
||
Child Thread
|
||
: 5
|
||
Child Thread
|
||
: 4
|
||
Main Thread
|
||
: 4
|
||
Child Thread
|
||
: 3
|
||
Child Thread
|
||
: 2
|
||
Main Thread
|
||
: 3
|
||
Child Thread
|
||
: 1
|
||
Exiting Child Thread ...
|
||
Main Thread
|
||
: 2
|
||
Main Thread
|
||
: 1
|
||
Exiting Main Thread ...
|
||
|
||
THREAD SYNCHRONIZATION
|
||
-
|
||
|
||
When threads share access to a common object, they can
|
||
conflict with each other. The shared access creates a problem.
|
||
This problem is often called a race condition.
|
||
|
||
-
|
||
|
||
To solve the problem use a lock mechanism. The lock mechanism
|
||
is used to control the threads that want to manipulate a
|
||
shared object.
|
||
|
||
THREAD SYNCHRONIZATION
|
||
-
|
||
|
||
To acquire the lock the code calls a synchronized method.
|
||
|
||
-
|
||
|
||
Methods that contain threed sensitive code are tagged with the
|
||
synchronized keyword.
|
||
|
||
-
|
||
|
||
When a thread calls a synchronized method on a shared object,
|
||
it owns that object’s lock until it returns from the method
|
||
and thereby unlocks the object.
|
||
|
||
-
|
||
|
||
When an object is locked by one thread, no other thread can
|
||
enter a synchronized method for that object, the other thread
|
||
is automatically deactivated, and it needs to wait until the
|
||
first thread has unlocked the object.
|
||
|
||
THREAD SYNCHRONIZATION
|
||
-
|
||
|
||
When multiple threads need to update information stored in a
|
||
shared object, some ordering has to be enforced to avoid
|
||
unintended consequences.
|
||
|
||
-
|
||
|
||
Java provides a locking mechanism for this purpose!
|
||
|
||
-
|
||
|
||
When one thread wants to access the shared object, it has to
|
||
|
||
-
|
||
|
||
-
|
||
|
||
Lock the object
|
||
|
||
-
|
||
|
||
Complete its operations on the object
|
||
|
||
-
|
||
|
||
Unlock the object
|
||
|
||
This way, each thread will have exclusive access to the object
|
||
when the thread needs the object
|
||
|
||
Thread Synchronization - No Threads!
|
||
public class CallMe {
|
||
public void call(String message) {
|
||
System.out.print("[ ");
|
||
System.out.print(message);
|
||
System.out.print(" ]");
|
||
}
|
||
}
|
||
|
||
public class SyncDemo {
|
||
public static void main(String[] args) {
|
||
CallMe target = new CallMe();
|
||
target.call("No threads!");
|
||
}
|
||
}
|
||
|
||
[ No threads! ]
|
||
|
||
Thread Synchronization - No Synchronization
|
||
public class CallMe {
|
||
public void call(String message) {
|
||
System.out.print("[ ");
|
||
System.out.print(message);
|
||
System.out.print(" ]");
|
||
}
|
||
}
|
||
|
||
public class SyncDemo {
|
||
public static void main(String[] args) {
|
||
CallMe target = new CallMe();
|
||
Runnable callerA = new Caller(target, "Hello");
|
||
Runnable callerB = new Caller(target, "World");
|
||
Runnable callerC = new Caller(target, "Howdy Y’all");
|
||
Thread threadA = new Thread(callerA);
|
||
Thread threadB = new Thread(callerB);
|
||
Thread threadC = new Thread(callerC);
|
||
threadA.start();
|
||
threadB.start();
|
||
threadC.start();
|
||
try {
|
||
threadA.join();
|
||
threadB.join();
|
||
threadC.join();
|
||
} catch (InterruptedException e) {
|
||
}
|
||
|
||
public class Caller implements Runnable {
|
||
String msg;
|
||
CallMe target;
|
||
public Caller(CallMe targ, String msg) {
|
||
this.target = target;
|
||
this.msg = msg;
|
||
}
|
||
@Override
|
||
public void run() {
|
||
target.call(msg);
|
||
}
|
||
}
|
||
|
||
The join() method
|
||
allows one thread to
|
||
wait until another
|
||
thread completes its
|
||
execution.
|
||
|
||
}
|
||
}
|
||
|
||
[ Hello[ Howdy Y'all ][ World ] ]
|
||
[ Hello ][ Howdy Y'all ][ World ]
|
||
[ Hello[ World ][ Howdy Y'all ] ]
|
||
|
||
Thread Synchronization - Method Synchronization
|
||
public class CallMe {
|
||
public synchronized void call(String message) {
|
||
System.out.print("[ ");
|
||
System.out.print(message);
|
||
System.out.print(" ]");
|
||
}
|
||
}
|
||
|
||
public class SyncDemo {
|
||
public static void main(String[] args) {
|
||
CallMe target = new CallMe();
|
||
Runnable callerA = new Caller(target, "Hello");
|
||
Runnable callerB = new Caller(target, "World");
|
||
Runnable callerC = new Caller(target, "Howdy Y’all");
|
||
Thread threadA = new Thread(callerA);
|
||
Thread threadB = new Thread(callerB);
|
||
Thread threadC = new Thread(callerC);
|
||
threadA.start();
|
||
threadB.start();
|
||
threadC.start();
|
||
try {
|
||
threadA.join();
|
||
threadB.join();
|
||
threadC.join();
|
||
} catch (InterruptedException e) {
|
||
}
|
||
|
||
public class Caller implements Runnable {
|
||
String msg;
|
||
CallMe target;
|
||
public Caller(CallMe targ, String msg) {
|
||
this.target = target;
|
||
this.msg = msg;
|
||
}
|
||
@Override
|
||
public void run() {
|
||
target.call(msg);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
[ Hello ][ Howdy Y'all ][ World ]
|
||
[ Hello ][ World ][ Howdy Y'all ]
|
||
|
||
Thread Synchronization - Block Synchronization
|
||
public class CallMe {
|
||
public void call(String message) {
|
||
System.out.print("[ ");
|
||
System.out.print(message);
|
||
System.out.print(" ]");
|
||
}
|
||
}
|
||
|
||
public class SyncDemo {
|
||
public static void main(String[] args) {
|
||
CallMe target = new CallMe();
|
||
Runnable callerA = new Caller(target, "Hello");
|
||
Runnable callerB = new Caller(target, "World");
|
||
Runnable callerC = new Caller(target, "Howdy Y’all");
|
||
Thread threadA = new Thread(callerA);
|
||
Thread threadB = new Thread(callerB);
|
||
Thread threadC = new Thread(callerC);
|
||
threadA.start();
|
||
threadB.start();
|
||
threadC.start();
|
||
try {
|
||
threadA.join();
|
||
threadB.join();
|
||
threadC.join();
|
||
} catch (InterruptedException e) {
|
||
}
|
||
|
||
public class Caller implements Runnable {
|
||
String msg;
|
||
CallMe target;
|
||
public Caller(CallMe targ, String msg) {
|
||
this.target = target;
|
||
this.msg = msg;
|
||
}
|
||
@Override
|
||
public void run() {
|
||
synchronized(target){
|
||
target.call(msg);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
[ Hello ][ Howdy Y'all ][ World ]
|
||
[ Hello ][ World ][ Howdy Y'all ]
|
||
|
||
DO YOU HAVE ANY
|
||
QUESTIONS?
|
||
|
||
THANK
|
||
YOU!
|
||
|
||
@
|
||
|
||
hend.alkittawi@utsa.edu
|
||
|
||
By Appointment
|
||
Online
|
||
|
||
|