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
|
|||
|
|
|||
|
|