0% found this document useful (0 votes)
193 views

Chapter 1 Multithreading

The document discusses threads and processes in multithreaded programming. It defines a thread as a single sequence of executable code within a larger program. Threads are lightweight and share resources, while processes are heavier and have separate resources. The main thread runs automatically, and additional threads can be created by extending the Thread class or implementing the Runnable interface. The run() method contains the code for the new thread.

Uploaded by

ebisa chibsa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
193 views

Chapter 1 Multithreading

The document discusses threads and processes in multithreaded programming. It defines a thread as a single sequence of executable code within a larger program. Threads are lightweight and share resources, while processes are heavier and have separate resources. The main thread runs automatically, and additional threads can be created by extending the Thread class or implementing the Runnable interface. The run() method contains the code for the new thread.

Uploaded by

ebisa chibsa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 66

1

CHAPTER ONE
MULTITHREADING

Mohammed M. MTech CSE


Computer Science Department
Madda Walabu University

Advanced Programming ITec3058


1. Threads and Processes
2

 Java provides built-in support for multithreaded programming.


 A multithreaded program contains two or more parts that can run
concurrently.
 Each part of such a program is called a thread, and each thread defines a
separate path of execution.
 Thus, multithreading is a specialized form of multitasking.
 A thread is a single sequence of executable code within a larger program.
 There are two distinct types of multitasking: process-based and thread-
based.
 A process is a program that is executing.
 Thus, process-based multitasking is the feature that allows your computer to
run two or more programs concurrently.
 For example, process-based multitasking enables you to run the Java
compiler at the same time that you are using a word processor and web
browser.
 In process-based multitasking, a program is the smallest unit of code that can
be dispatched by the scheduler.
1. Threads and Processes…
3

 In a thread-based multitasking environment, the thread is the smallest unit of


dispatchable code.
 This means that a single program can perform two or more tasks
simultaneously.
E.g. word processor can format text at the same time that it is printing.
 Multitasking threads require less overhead than multitasking processes.
 Processes are heavyweight tasks that require their own separate address
spaces.
 Inter-process communication is expensive and limited.
 Context switching from one process to another is also costly.
 Threads, on the other hand, are lightweight.
 They share the same address space and cooperatively share the same
heavyweight process.
 Inter-thread communication is inexpensive, and context switching from one
thread to the next is low cost.
 Process-based multitasking is not under the control of Java, but under OS.
 However, multithreaded multitasking is under Java.
1. Threads and Processes…
4

 Multithreading enables you to write very efficient programs that make


maximum use of the CPU, because idle time can be kept to a minimum.
 This is especially important for the interactive, networked environment in
which Java operates, because idle time is common.
For example, the transmission rate of data over a network is much slower
than the rate at which the computer can process it.
 Even local file system resources are read and written at a much slower pace
than they can be processed by the CPU.
 And user input is much slower than the computer.

 In a traditional single-threaded environment, your program has to wait for


each of these tasks to finish before it can proceed to the next one.
 Multithreading lets you gain access to this idle time and put it to good use.

 In general, in concurrent programming, there are two basic units of


execution: processes and threads.
 In the Java programming language, concurrent programming is mostly
concerned with threads.
1. Threads and Processes…
5

Processes
 A process has a self-contained execution environment.
 A process generally has a complete, private set of basic run-time
resources; in particular, each process has its own memory space.
 Processes are often seen as synonymous with programs or
applications.
 However, what the user sees as a single application may in fact be a
set of cooperating processes.
 To facilitate communication between processes, most operating
systems support Inter Process Communication (IPC) resources, such as
pipes and sockets.
 IPC is used not just for communication between processes on the same
system, but processes on different systems.
1. Threads and Processes…
6

Threads
 Threads are sometimes called lightweight processes.
 Both processes and threads provide an execution environment, but creating
a new thread requires fewer resources than creating a new process.
 Threads exist within a process — every process has at least one thread.
 Threads share the process's resources, including memory and open files.
 This makes for efficient, but potentially problematic, communication.
 Multithreaded execution is an essential feature of the Java platform.
 Every application has at least one thread — or several, if you count system
threads that do things like memory management and signal handling.
 But from the application programmer's point of view, you start with just one
thread, called the main thread.
 This thread has the ability to create additional threads, as we'll demonstrate
in the next section.
2. The Main Thread
7

 When a Java program starts up, one thread begins running immediately.
 This is called the main thread of your program, because it is the one that is
executed when your program begins.
 The main thread is important for two reasons:
 It is the thread from which other “child” threads will be spawned.
 Often it must be the last thread to finish execution because it performs
various shutdown actions.
 Although the main thread is created automatically when your program is
started, it can be controlled through a Thread object.
 To do so, you must obtain a reference to it by calling the method
currentThread() of Thread class:
static Thread currentThread( )
 This method returns a reference to the thread in which it is called.
 Once you have a reference to the main thread, you can control it just like
any other thread.
public static void main(String args[]) {
Thread t = Thread.currentThread();
System.out.println("Current thread: " + t);
8 //change the name of the thread
t.setName("Main Thread");
System.out.println("After name change: " + t);
try {
for (int n = 5; n > 0; n--) { • When thread t is converted to string,
System.out.println(n); it returns a thread name, its priority
Thread.sleep(1000); & thread group name.
} Output:
} catch (InterruptedException e) { Current thread: Thread[main,5,main]
After name change: Thread[Main
System.out.println("Main thread
Thread,5,main]
interrupted");
5
} 4
} 3
2
1
3. Creating a Thread
9

 An application that wants to create a thread must provide the


code that will run in that thread.
 There are two ways to create a thread:
 Extending the Thread class
 Implementing the Runnable interface
 To create a thread, your program has to either extend Thread
or implement the Runnable interface.
3. Creating a Thread…
10

A. Thread Class
 The Thread class lets you create an object that can be run as a thread in a
multi-threaded Java application.
 One way to create a thread is to create a class that extends Thread class.
 The extending class must override the run() method, which is the entry point
for the new thread.
 It must also call start() to begin execution of the new thread.
 Commonly used constructors of Thread class:
Thread()
Thread(String name)
Thread(Runnable r)
Thread(Runnable r, String name)
Thread(ThreadGroup group, Runnable r)
Thread(ThreadGroup group, Runnable r, String name)
3. Creating a Thread…
11

 The Thread class defines several methods that help manage threads.
Method Meaning
static int activeCount() Returns the number of active threads.
Fills the specified array with a copy of each active thread. The
static int enumerate(Thread[] t)
return value is the number of threads added to the array.
String getName() Returns the name of the thread.
int getPriority() Returns the thread’s priority.
void interrupt() Interrupts this thread.
boolean interrupted() Checks to see if the thread has been interrupted.
void setPriority(int priority) Sets the thread’s priority.
void setName(String name) Sets the thread’s name.
static void sleep(int Causes the currently executing thread to sleep for the specified
milliseconds) number of milliseconds.
This method is called when the thread is started. Place the code
void run()
that you want the thread to execute inside this method.
void start() Starts the thread.
This causes the current thread to move from the running state to
static void yield()
the ready state, so that other threads may get a chance to run.
3. Creating a Thread…
12

 Example: creating simple thread


public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
HelloThread ht = new HelloThread();
ht.start();
}
}
 When the start method is called, the thread starts execution by executing the
code inside the run() method of the thread.
 The run() method is not called directly by programs rather called internally
by the thread itself.
 All we have to do to start a thread is call the start() method.
3. Creating a Thread…
13

 Java assigns every thread a priority.


 Thread priorities are used by the thread scheduler to decide when each
thread should be allowed to run.
 In theory, higher-priority threads get more CPU time than lower-priority
threads.
 By default, a thread inherits the priority of the thread that spawned it.
 You can set priority of any thread by using the setPriority(int) method, and
you can get the thread’s priority by using the getPriority() method.
 Priorities are numbers ranging from 1 to 10.
 The Thread class has the int constants MIN_PRIORITY, NORM_PRIORITY, and
MAX_PRIORITY, representing 1, 5, and 10, respectively.
 The priority of the main thread is Thread.NORM_PRIORITY.
 The JVM always picks the currently runnable thread with the highest priority.
 A lower priority thread can run only when no higher-priority threads are
running.
 If all runnable threads have equal priorities, each is assigned an equal
portion of the CPU time in a circular queue.
Example: setting thread priority
14

public class ThreadPriority { class OneThread extends Thread {


public static void main(String args[]) { String name;
Thread tt1 = new OneThread("1"); OneThread(String nm) {
Thread tt2 = new OneThread("2"); name = nm;
tt1.setPriority(8); }
tt2.setPriority(2); public void run() {
for (int i = 1; i <= 50; i++) {
tt1.start(); System.out.println("Child
tt2.start(); Thread" + name + ": " + i);
} }
} System.out.println("Exiting child
thread " + name + ".");
}
}
3. Creating a Thread…
15

B. Runnable Interface
 The easiest way to create a thread is to create a class that
implements the Runnable interface.
 Runnable abstracts a unit of executable code.

 You can create a thread on any object that implements Runnable.

 To implement Runnable, a class need only implement a single method


called run(), which is declared like this:
public void run()
 Inside run(), you will write the code that constitutes the new thread.

 The run() method can call other methods, use other classes, and
declare variables, just like the main thread can.
 The only difference is that run() establishes the entry point for
another, concurrent thread of execution within your program.
 This thread will end when run() returns.
3. Creating a Thread…
16

 To run it, you will instantiate an object of type Thread passing the object of
the class that implements the Runnable interface as parameter.
 Thread defines several constructors for this.
Thread(Runnable threadObj)
Thread(Runnable threadOb, String threadName)
 In the constructor, threadObj is an instance of a class that implements the
Runnable interface.
 This defines where execution of the thread will begin.
 The name of the new thread is specified by threadName.
 After the new thread is created, it will not start running until you call its
start() method, which is declared within Thread.
 In essence, start() executes a call to run().
void start( )
Example: creating thread using Runnable
class TestThread implements Runnable {
public void run() {
17 try {
for(int i = 5; i > 0; i--) {
System.out.println("Child Thread: " + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Child interrupted.");
}
System.out.println("Exiting child thread.");
}
}
class ThreadDemo {
public static void main(String args[]) {
Thread tt = new Thread(new TestThread());
tt.start();
Creating thread using Runnable …
18

try {  Output: (the output on your


computer may differ)
for(int i = 5; i > 0; i--) {
System.out.println("Main Child thread: Thread[Demo
Thread,5,main]
Thread: " + i);
Main Thread: 5
Thread.sleep(1000);
Child Thread: 5
}
Child Thread: 4
} catch (InterruptedException
Main Thread: 4
e) {
Child Thread: 3
System.out.println("Main
thread interrupted."); Child Thread: 2
Main Thread: 3
}
Child Thread: 1
System.out.println("Main
thread exiting."); Exiting child thread.
Main Thread: 2
}
Main Thread: 1
}
Main thread exiting.
3. Creating a Thread…
19

Which approach is better?


 Of the two ways to create threads, which approach is better?
 The answers to that question turn on the same point.
 The Thread class defines several methods that can be overridden by a
derived class.
 Of these methods, the only one that must be overridden is run().
 This is, of course, the same method required when you implement Runnable.
 Many Java programmers feel that classes should be extended only when
they are being enhanced or modified in some way.
 So, if you will not be overriding any of Thread’s other methods, it is
probably best simply to implement Runnable.
 Also, by implementing Runnable, your thread class does not need to inherit
Thread, making it free to inherit from other class.
 Ultimately, which approach to use is up to you.
4. Controlling Thread
20

I. Pausing Execution with Sleep


 Thread.sleep(length) causes the current thread to suspend execution for a
specified period.
 This is an efficient means of making processor time available to the other
threads or other applications.
 The sleep method can also be used for pacing, and waiting for another
thread with duties that are understood to have time requirements.
 Two overloaded versions of sleep are provided:

 one that specifies the sleep time in milliseconds and


 one that specifies the sleep time to milliseconds and nanoseconds.

 However, these sleep times are not guaranteed to be precise, because they
are limited by the facilities provided by the underlying OS.
 Also, the sleep period can be terminated by interrupts.

 So, you cannot assume that invoking sleep will suspend the thread for
precisely the time period specified.
Example: main thread uses sleep to print messages at four-second intervals:
public class SleepMessage extends Thread{
public void run(){
21
String info[] = {"Mares eat oats", "Dogs eat oats", "Little lambs eat ivy",
"A kid will eat ivy too"};
for (int i = 0; i < info.length; i++) {
try {
Thread.sleep(4000); //sleep 4 seconds
System.out.println(info[i]);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public static void main(String args[]) throws InterruptedException {
SleepMessage sm = new SleepMessage();
sm.start();
}
}
4. Controlling Thread…
22

II. Interrupts
 An interrupt is an indication to a thread that it should stop what it is doing and
do something else.
 It's up to the programmer to decide exactly how a thread responds to an
interrupt, but it is very common for the thread to terminate.
 A thread sends an interrupt by invoking interrupt() on the Thread object for the
thread to be interrupted.
 For the interrupt mechanism to work correctly, the interrupted thread must
support its own interruption.
 How to support interrupt depends on what it's currently doing.
 If the thread is frequently invoking methods that throw InterruptedException, it
simply returns from the run method after it catches that exception.
4. Controlling Thread…
23

public void run() {


for (int i = 0; i < importantInfo.length; i++) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
//thread is interrupted: no more messages.
return;
}
System.out.println(info[i]);
}
}
 Many methods that throw InterruptedException, such as sleep, are
designed to cancel their current operation and return immediately
when an interrupt is received.
4. Controlling Thread…
24

 What if a thread goes a long time without invoking a method that


throws InterruptedException?
 Then it must periodically invoke Thread.interrupted() method, which
returns true if an interrupt has been received.
 For example:
public void run() {
for (int i = 0; i < inputs.length; i++) {
heavyCrunch(inputs[i]);
if (Thread.interrupted()) {
//thread is interrupted: no more crunching.
return;
}
}
}
4. Controlling Thread…
25

III. Joins
 It is not uncommon for one thread to need the result of another thread.
 For example, a web browser loading an HTML page in one thread might create
a second thread to retrieve every image embedded in the page.
 Java provides three join() methods to allow one thread to wait for another
thread to finish before continuing. These are:
void join() throws InterruptedException
void join(long milliseconds) throws InterruptedException
void join(long milliseconds, int nanoseconds) throws InterruptedException
 The first variant waits indefinitely for the joined thread to finish.
 The second two variants wait for the specified amount of time, after which they
continue even if the joined thread has not finished.
 As with the sleep() method, nanosecond accuracy is not guaranteed.
4. Controlling Thread…
26

 The joining thread (i.e., the one that executes the join() method) waits for the
joined thread (i.e, the one whose join() method is invoked) to finish.
 The thread on which the join() is executed is said to have joined the thread
whose name is used to call the join() method.
 The join() method allows one thread to wait for the completion of another.
 The join() method waits for a thread to die.
 In other words, it causes the currently running thread to stop executing until the
thread it joins with completes its task.
4. Controlling Thread…
27

class TestThread implements } catch (InterruptedException e) {


Runnable { System.out.println("Child
String name; interrupted.");
TestThread(String nm) { }
name = nm; System.out.println("Exiting
} child thread " + name + ".");
public void run() { }
try { }
for (int i = 5; i > 0; i--) { class ThreadDemo {
public static void main(String
System.out.println("Child Thread" args[]) {
+ name + ": " + i); Thread tt1 = new
Thread.sleep(1000); Thread(new TestThread("1"));
} tt1.start();
try { Output:
Child Thread1: 5
tt1.join();
Child Thread1: 4
28 } catch (InterruptedException ex) { Child Thread1: 3
System.out.println("Thread Child Thread1: 2
Child Thread1: 1
interrupted");
Exiting child thread 1.
} Main Thread: 5
Thread tt2 = new Thread(new Main Thread: 4
Main Thread: 3
TestThread("2"));
Main Thread: 2
tt2.start(); Main Thread: 1
for (int i = 5; i > 0; i--) { Main thread exiting.
Child Thread2: 5
System.out.println("Main Thread: " + Child Thread2: 4
i); Child Thread2: 3
} Child Thread2: 2
Child Thread2: 1
System.out.println("Main thread Exiting child thread 2.
exiting.");
}
}
4. Controlling Thread…
29

IV. Inter-thread communication: wait(), notify() and notifyAll()


 The Object class in Java has three final methods that allow threads to
communicate about the locked status of a resource.
A. wait()
 It tells the calling thread to give up the lock and go to sleep until
some other thread enters the same monitor and calls notify().
 The wait() method releases the lock prior to waiting and reacquires
the lock prior to returning from the wait() method.
 The wait() method is actually tightly integrated with the
synchronization lock, using a feature not available directly from the
synchronization mechanism.
 In other words, it is not possible for us to implement the wait() method
purely in Java: it is a native method.
4. Controlling Thread…
30

 General syntax for calling wait() method is like this:


synchronized( lockObject ) {
while( ! condition ) {
lockObject.wait();
}
//take the action here;
}
B. notify()
 It wakes up one single thread that called wait() on the same object.
 It should be noted that calling notify() does not actually give up a lock on a
resource.
 It tells a waiting thread that that thread can wake up.
 However, the lock is not actually given up until the notifier’s synchronized
block has completed.
4. Controlling Thread…
31

 General syntax for calling notify() method is like this:


synchronized(lockObject) {
//establish_the_condition;
lockObject.notify();
//any additional code if needed
}

C. notifyAll()
 It wakes up all the threads that called wait() on the same object.

 The highest priority thread will run first in most of the situation, though not
guaranteed.
 Other things are same as notify() method above.

synchronized(lockObject) {
establish_the_condition;
lockObject.notifyAll();
}
class Customer { class Test {
int amount = 10000; public static void main(String args[]) {
synchronized void withdraw(int amount) { final Customer c = new Customer();
System.out.println("going to withdraw..."); Thread t1 = new Thread() {
32
if (this.amount < amount) { public void run() {
System.out.println("Less balance, c.withdraw(15000);
waiting for deposit"); }
try { };
wait(); t1.start();
} catch (Exception e) {} Thread t2 = new Thread() {
} public void run() {
this.amount -= amount; c.deposit(10000);
System.out.println("withdraw completed..."); }
} };
synchronized void deposit(int amount) { t2.start();
System.out.println("going to deposit..."); }
this.amount += amount; }
System.out.println("deposit completed... ");
notify();
}
5. Thread States: Life Cycle of a Thread
33

 At any time, a thread can be in one of several thread states.


 Generally, threads can be in one of five states: born, ready, running,
blocked, or finished.
 A thread that was just created is in the born state.
 The thread remains in this state until the program calls the thread’s start
method, which causes the thread to enter the ready state (also known as the
runnable state).
 The highest priority ready thread enters the running state, when the system
assigns a processor to the thread.
 A thread enters the dead state when its run method completes or terminates
for any reason — a dead thread eventually will be disposed of by system.
 A running thread may enter a blocked state.
 One common way for a running thread to enter the blocked state is when
the thread issues an input/output request.
 In this case, a blocked thread becomes ready when the I/O for which it is
waiting completes.
5. Thread States: Life Cycle…
34
5. Thread States: Life Cycle…
35

 A thread can enter the Blocked state (i.e., become inactive) for several
reasons.
 It may have invoked the join(), sleep(), or wait() method, or some other
thread may have invoked these methods.
 It may be waiting for an I/O operation to finish.
 A blocked thread may be reactivated when the action inactivating it is
reversed.
 For example, if a thread has been put to sleep and the sleep time has
expired, the thread is reactivated and enters the Ready state.
 Finally, a thread is finished if it completes the execution of its run() method.
5. Thread States: Life Cycle…
36

 When a running thread calls wait(), the thread enters a waiting state for the
particular object on which wait() was called.
 One thread in the waiting state for a particular object becomes ready on a
call to notify() issued by another thread associated with that object.
 Every thread in the waiting state for a given object becomes ready on a call
to notifyAll() by another thread associated with that object.
 A thread enters the dead state when its run() method either completes or
throws an uncaught exception.
 The isAlive() method is used to find out the state of a thread.
 It returns true if a thread is in the ready, blocked, or running state; it returns
false if a thread is new and has not started or if it is finished.
6. Synchronization
37

 Threads communicate primarily by sharing access to fields and the objects


reference fields refer to.
 This form of communication is extremely efficient, but makes two kinds of
errors possible:
 thread interference and

 memory consistency errors

 The tool needed to prevent these errors is synchronization.


 When two or more threads need access to a shared resource, they need
some way to ensure that the resource will be used by only one thread at a
time.
 The process by which this is achieved is called synchronization.
6. Synchronization…
38

 Look at the following example.


public class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
}
 Counter is designed so that each invocation of increment will add 1 to c and
each invocation of decrement will subtract 1 from c.
 However, if a Counter object is referenced from multiple threads, interference
between threads may prevent this from happening as expected.
 Interference happens when two operations, running in different threads, but
acting on the same data, interleave.
 This means that the two operations consist of multiple steps, and the sequences
of steps overlap.
 It might not seem possible for operations on instances of Counter to interleave, since
the add operation on c are single, simple statements.
 However, even simple statements can translate to multiple steps by the virtual

39
machine.
 A single increment expression c++ can be decomposed into three steps:

1. Retrieve the current value of c.


2. Increment the retrieved value by 1.
3. Store the incremented value back in c.
 Suppose Thread A invokes increment at about the same time Thread B

invokes decrement.
 If the initial value of c is 0, they might interleave as follows:

1. Thread A: Retrieve c.
2. Thread B: Retrieve c.
3. Thread A: Increment retrieved value; result is 1.
4. Thread B: Decrement retrieved value; result is -1.
5. Thread A: Store result in c; c is now 1.
6. Thread B: Store result in c; c is now -1.
 Thread A's result is lost, overwritten by Thread B.
6. Synchronization…
40

 This particular interleaving is only one possibility.


 Under different circumstances it might be Thread B's result that gets lost, or
there could be no error at all.
 Thread interference bugs can be difficult to detect and fix.

Memory consistency errors


 Memory consistency errors occur when different threads have inconsistent
views of what should be the same data.
 The key to avoiding memory consistency errors is understanding
the happens-before relationship.
 This relationship is simply a guarantee that memory writes by one specific
statement are visible to another specific statement.
 Suppose a simple int field is defined and initialized:
int counter = 0;
6. Synchronization…
41

 The counter field is shared between two threads, A and B.


 Suppose thread A increments counter:
counter++;
 Then, shortly afterwards, thread B prints out counter:
System.out.println(counter);
 If the two statements had been executed in the same thread, the value
printed out would be "1".
 But if the two statements are executed in separate threads, the value printed
out might well be "0", because there's no guarantee that thread A's change
to counter will be visible to thread B — unless the programmer has
established a happens-before relationship between these two statements.
 There are several actions that create happens-before relationships.
 One of them is synchronization.
 Generally, When two or more threads need access to a shared resource,
they need some way to ensure that the resource will be used by only one
thread at a time.
6. Synchronization…
42

Example: if you run this program, you will get unexpected output
because of thread interference and memory consistency errors
class ThreadError extends Thread { public static void main(String[] args)
static int count = 0; throws InterruptedException {
public void run() { ThreadError t1 = new
ThreadError();
for (int x = 0; x < 10000;
x++) { ThreadError t2 = new
ThreadError();
count++;
t1.start();
count--;
t2.start();
}
}
System.out.println(this.getName() + "
count: " + count); }
}
6. Synchronization…
43

 The process by which this is achieved is called synchronization.


 Key to synchronization is the concept of the monitor (also called a
semaphore).
 A monitor is an object that is used as a mutually exclusive lock, or mutex.
 Only one thread can own a monitor at a given time.
 When a thread acquires a lock, it is said to have entered the monitor.
 All other threads attempting to enter the locked monitor will be suspended
until the first thread exits the monitor.
 These other threads are said to be waiting for the monitor.
 A thread that owns a monitor can reenter the same monitor if it so desires.
 Java provides two basic synchronization methods:
 synchronized methods and

 synchronized statements.
6. Synchronization…
44

A. Synchronized Methods
 To avoid thread interference and memory consistency errors, it is necessary
to prevent more than one thread from simultaneously entering a certain part
of the program, known as the critical region.
 You can use the keyword synchronized to synchronize the method so that
only one thread can access the method at a time.
 Synchronized methods is a simple strategy for preventing thread
interference and memory consistency errors.
 Synchronization is easy in Java, because all objects have their own implicit
object monitor associated with them.
 To enter an object’s monitor, just call a method that has been modified with
the synchronized keyword.
 While a thread is inside a synchronized method, all other threads that try to
call it on the same instance have to wait.
 To exit the monitor & relinquish control of the object to another waiting
thread, the owner of monitor simply returns from the synchronized method.
 To make a method synchronized, simply add the synchronized keyword to its
declaration:
public class Counter {
private int c = 0;
45 public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
}
 If count is an instance of Counter, then making these methods synchronized
has two effects:
 First, it is not possible for two invocations of synchronized methods on the
same object to interleave.
 When one thread is executing a synchronized method for an object, all other
threads that invoke synchronized methods for the same object blocks until the
first thread is done with the object.
 Second, when a synchronized method exits, it automatically establishes a
happens-before relationship with any subsequent invocation of a
synchronized method for the same object.
 This guarantees that changes to the state of the object are visible to all
threads.
Example: using synchronized methods
46

public class Counter { class CounterThread extends Thread


private int c = 0; {
public synchronized void static Counter sc = new Counter();
increment() { String name;
c++; public CounterThread(String nn) {
} name = nn;
public synchronized void }
decrement() { public void run() {
c--; for(int i = 0; i < 10; i++) {
} sc.increment();
public synchronized int value() { System.out.println("Thread "
return c; + name + ": " + sc.value());
} }
}
Example: using synchronized methods ...
47

for(int i = 0; i < 5; i++) { CounterThread ct1 = new


sc.decrement(); CounterThread("1");
CounterThread ct2 = new
System.out.println("Thread " + CounterThread("2");
name + ": " + sc.value()); ct1.start();
} ct2.start();
} }
} }
class SyncTest {
public static void main(String
args[]) {
6. Synchronization…
48

 A synchronized method acquires a lock before it executes.


 In the case of an instance method, the lock is on the object for
which the method was invoked.
 In the case of a static method, the lock is on the class.
 If one thread invokes a synchronized instance method on an
object, the lock of that object is acquired first, then the method
is executed, and finally the lock is released.
6. Synchronization…
49

B. Synchronized Statements
 Another way to create synchronized code is with synchronized
statements.
 Synchronized statements enable you to synchronize part of the code
in a method instead of the entire method.
 This increases concurrency.

synchronized(object) {
// statements to be synchronized
}
 Here, object is a reference to the object being synchronized.

 A synchronized block ensures that a call to a method that is a

member of object occurs only after the current thread has successfully
entered object’s monitor.
6. Synchronization…
50

 Unlike synchronized methods, synchronized statements must specify the


object that provides the intrinsic lock:
class Person {
String lastName;
static int nameCount = 0;
ArrayList nameList;
public Person() {
nameList = new ArrayList();
}
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
}
6. Synchronization…
51

 Synchronized statements are also useful for improving concurrency with fine-
grained synchronization.
 Suppose, for example, class MsCounter has two instance fields, c1 and c2,
that are never used together.
 All updates of these fields must be synchronized, but there's no reason to
prevent an update of c1 from being interleaved with an update of c2 —
and doing so reduces concurrency by creating unnecessary blocking.
 Instead of using synchronized methods or otherwise using the lock associated
with this, we create two objects solely to provide locks.
6. Synchronization…
52

public class MsCounter {


private long c1 = 0, c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
 Use this approach with extreme care.

 You must be absolutely sure that it really is safe to interleave access of the affected
fields.
7. Deadlock
53

 A special type of error that you need to avoid that relates specifically to
multitasking is deadlock.
 Deadlock occurs when two threads have a circular dependency on a pair of
synchronized objects.
 Deadlock describes a situation where two or more threads are blocked
forever, waiting for each other.
 For example, suppose one thread enters the monitor on object X and
another thread enters the monitor on object Y.
 If the thread in X tries to call any synchronized method on Y, it will block as
expected.
 However, if the thread in Y, in turn, tries to call any synchronized method on
X, the thread waits forever, because to access X, it would have to release its
own lock on Y so that the first thread could complete.
 Deadlock is a difficult error to debug for two reasons:
 In general, it occurs only rarely, when the two threads time-slice in just the
right way.
 It may involve more than two threads and two synchronized objects.
7. Deadlock
54

class A { String name =


synchronized void foo(B b) { Thread.currentThread().getName();
String name = System.out.println(name + "
Thread.currentThread().getName(); trying to call A.last()");
System.out.println(name + " a.last();
trying to call B.last()"); }
b.last(); synchronized void last() {
} System.out.println("Inside
synchronized void last() { B.last");
System.out.println("Inside }
A.last"); }
} public class DeadLock implements
} Runnable {
class B { A a = new A();
synchronized void bar(A a) { B b = new B();
DeadLock() {
Thread.currentThread().setName("MainThread");
Thread t = new Thread(this, "RacingThread");
t.start();
55 a.foo(b); // get lock on a in this thread.
System.out.println("Back in main thread");
}
public void run() {
b.bar(a); // get lock on b in other thread.
System.out.println("Back in other thread");
}
public static void main(String args[]) {
new DeadLock();
}
}
 This produces the following output and goes into deadlock:
RacingThread trying to call A.last()
MainThread trying to call B.last()
 Deadlock is easily avoided by using a simple technique known as resource ordering.
 With this technique, you assign an order to all the objects whose locks must be
acquired and ensure that each thread acquires the locks in that order.
7. Deadlock…
56

Producer/Consumer Problem
 The producer-consumer problem (also known as the bounded-buffer
problem) is another classical example of a multithread synchronization
problem.
 The problem describes two threads, the producer and the consumer, who
share a common, fixed-size buffer.
 The producer’s job is to generate a piece of data and put it into the buffer.

 The consumer is consuming the data from the same buffer simultaneously.

 The problem is to make sure that the producer will not try to add data into
the buffer if it is full and that the consumer will not try to remove data from
an empty buffer.
 The solution for this problem involves two parts.

 The producer should wait when it tries to put the newly created product into
the buffer until there is at least one free slot in the buffer.
 The consumer, on the other hand, should stop consuming if the buffer is
empty.
7. Deadlock…
57

 To synchronize the operations, use a lock with two conditions:


 notEmpty (i.e., buffer is not empty) and

 notFull (i.e., buffer is not full).

 When a task adds an int to the buffer, if the buffer is full, the task will wait
for the notFull condition.
 When a task deletes an int from the buffer, if the buffer is empty, the task
will wait for the notEmpty condition.
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
p1.start();
58 c1.start();
}
}
class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e) {}
}
available = false;
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e) { }
}
contents = value;
available = true;
notifyAll();
}
}
class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int num) {
cubbyhole = c;
number = num;
59 }
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumer #" + this.number + " got: " + value);
}
}
}
class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" + this.number + " put: " + i);
try {
Thread.sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
8. Event Dispatching Thread
60

 Swing components are not designed for a multithreaded environment.


 Swing event handling code runs on a special thread known as the event
dispatching thread.
 Most code that invokes Swing methods also runs on this thread.
 This is necessary because most Swing object methods are not "thread safe":
invoking them from multiple threads risks thread interference or memory
consistency errors.
 All other Swing component methods must be invoked from the event dispatch
thread.
 Programs that ignore this rule may function correctly most of the time, but
are subject to unpredictable errors that are difficult to reproduce.
 After Swing components have been displayed on the screen, they should
only be operated on by the event dispatching thread.
8. Event Dispatching Thread…
61

 The event dispatching thread is started automatically by the JVM when an


application has a GUI.
 The event dispatching thread calls methods like paint() on Component,
actionPerformed() on ActionListener, and all of the other event-handling
methods.
 Hence, most of the time, modifications to Swing components are done in the
event-handling methods.
 Because the event thread calls these methods, it is perfectly safe to directly
change components in event-handling code.
 If you are in another thread and want to modify GUI components, you have
to make sure that the code is executed on event dispatching thread.
 To enable the GUI code to be executed on the event dispatching thread, you
must use one of two methods that are defined by the SwingUtilities class.
 These methods are invokeLater() and invokeAndWait(). They are shown here:
static void invokeLater(Runnable obj)
static void invokeAndWait(Runnable obj)
8. Event Dispatching Thread…
62

 The static method invokeAndWait() that can be used to put references to


blocks of code onto the event queue:
static void invokeAndWait(Runnable target) throws InterruptedException,
InvocationTargetException
 The parameter target is a reference to an instance of Runnable.
 A new thread is not created when Runnable is used with
SwingUtilities.invokeAndWait().
 The event thread will end up calling the run() method of the Runnable when
its turn comes up on the event queue.
Example: using SwingUtilities.invokeAndWait() to modify JLabel content
public class InvokeAndWaitDemo extends Object {
public static void main(String[] args) {
final JLabel label = new JLabel("————");
JPanel panel = new JPanel(new FlowLayout());
63
panel.add(label);

JFrame f = new JFrame("InvokeAndWaitDemo");


f.add(panel);
f.setSize(300, 100);
f.setVisible(true);

try {
Thread.sleep(3000);
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
label.setText("New text!");
}
});
} catch (InterruptedException ix) {
System.out.println("interrupted while waiting on invokeAndWait()");
} catch (InvocationTargetException x) {
System.out.println("exception thrown from run()");
}
}
}
8. Event Dispatching Thread…
64

 The SwingUtilities.invokeLater() method works like


SwingUtilities.invokeAndWait() except for the fact that it puts the request on
the event queue and returns right away.
 The invokeLater() method does not wait for the block of code inside the
Runnable referred to by target to execute.
 This allows the thread that posted the request to move on to other activities.
 Just as with invokeAndWait(), a new thread is not created when Runnable is
used with SwingUtilities.invokeLater().
class EventDemo extends JFrame{
JLabel jlab;
EventDemo() {
setLayout(new FlowLayout());
setSize(220, 90);
65 setVisible(true);
JButton jbtnAlpha = new JButton("Alpha");
JButton jbtnBeta = new JButton("Beta");
jbtnAlpha.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
jlab.setText("Alpha was pressed.");
}
});
jbtnBeta.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
jlab.setText("Beta was pressed.");
}
});
jfrm.add(jbtnAlpha);
jfrm.add(jbtnBeta);
jlab = new JLabel("Press a button.");
jfrm.add(jlab);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
EventDemo dm = new EventDemo();
}
});
}
}
8. Event Dispatching Thread…
66

 It is not always necessary to use invokeAndWait() and invokeLater() to


interact with Swing components.
 Any thread can safely interact with the components before they become
visible on the screen.
 You have seen this already in the examples: the main thread constructs the
GUI and then invokes setVisible().
 After the components are drawn to the screen, only the event thread should
make further changes to their appearance.
 Besides, any code that is executed inside event handlers like ActionListener,
KeyListener, etc. is by default executed on event dispatch thread.
 So, in such cases, there no need to use invokeAndWait() or invokeLater() to
change Swing components.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy