Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas


Capítulo 32 - Programación Multiproceso y Paralelo - Liang-Java-Comp11e, Resumos de Informática

Programación Multiproceso y Paralelo

Tipologia: Resumos

2018

Compartilhado em 21/07/2018

dddmmm80-5
dddmmm80-5 🇧🇷

4.8

(4)

13 documentos

1 / 42

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
Objectives
To get an overview of multithreading (§32.2).
To develop task classes by implementing the Runnable interface
(§32.3).
To create threads to run tasks using the Thread class (§32.3).
To control threads using the methods in the Thread class (§32.4).
To control animations using threads and use Platform.runLater to
run the code in the application thread (§32.5).
To execute tasks in a thread pool (§32.6).
To use synchronized methods or blocks to synchronize threads to avoid
race conditions (§32.7).
To synchronize threads using locks (§32.8).
To facilitate thread communications using conditions on locks (§§32.9
and 32.10).
To use blocking queues (ArrayBlockingQueue, LinkedBlocking-
Queue, and PriorityBlockingQueue) to synchronize access to a
queue (§32.11).
To restrict the number of concurrent accesses to a shared resource using
semaphores (§32.12).
To use the resource-ordering technique to avoid deadlocks (§32.13).
To describe the life cycle of a thread (§32.14).
To create synchronized collections using the static methods in the
Collections class (§32.15).
To develop parallel programs using the Fork/Join Framework (§32.16).
Multithreading
and Parallel
Programming
CHAPTER
32
M32_LIAN0182_11_SE_C32.indd 1 5/29/17 7:26 AM
© 2018 Pearson Education, Inc., Hoboken, NJ. All rights reserved.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a

Pré-visualização parcial do texto

Baixe Capítulo 32 - Programación Multiproceso y Paralelo - Liang-Java-Comp11e e outras Resumos em PDF para Informática, somente na Docsity!

Objectives

■ ■ To get an overview of multithreading (§32.2).

■ ■ To develop task classes by implementing the Runnable interface

■ ■ To create threads to run tasks using the Thread class (§32.3).

■ ■ To control threads using the methods in the Thread class (§32.4).

■ ■ To control animations using threads and use Platform.runLater to

run the code in the application thread (§32.5).

■ ■ To execute tasks in a thread pool (§32.6).

■ ■ To use synchronized methods or blocks to synchronize threads to avoid

race conditions (§32.7).

■ ■ To synchronize threads using locks (§32.8).

■ ■ To facilitate thread communications using conditions on locks (§§32.

and 32.10).

■ ■ To use blocking queues ( ArrayBlockingQueue , LinkedBlocking-

Queue , and PriorityBlockingQueue ) to synchronize access to a

queue (§32.11).

■ ■ To restrict the number of concurrent accesses to a shared resource using

semaphores (§32.12).

■ ■ To use the resource-ordering technique to avoid deadlocks (§32.13).

■ ■ To describe the life cycle of a thread (§32.14).

■ ■ To create synchronized collections using the static methods in the

Collections class (§32.15).

■ ■ To develop parallel programs using the Fork/Join Framework (§32.16).

Multithreading

and Parallel

Programming

CHAPTER

32-2 Chapter 32 Multithreading and Parallel Programming

32.1 Introduction

Multithreading enables multiple tasks in a program to be executed concurrently.

One of the powerful features of Java is its built-in support for multithreading— the concurrent

running of multiple tasks within a program. In many programming languages, you have to

invoke system-dependent procedures and functions to implement multithreading. This chapter

introduces the concepts of threads and how multithreading programs can be developed in Java.

32.2 Thread Concepts

A program may consist of many tasks that can run concurrently. A thread is the flow

of execution, from beginning to end, of a task.

A thread provides the mechanism for running a task. With Java, you can launch multiple

threads from a program concurrently. These threads can be executed simultaneously in multi-

processor systems, as shown in Figure 32.1a.

Point multithreading^ Key

Point

Key

thread task

Figure 32.1 (a) Multiple threads running on multiple CPUs. (b) Multiple threads share a

single CPU.

Thread 1

Thread 3

Thread 2

(a)

Thread 1

Thread 3

Thread 2

(b)

In single-processor systems, as shown in Figure 32.1b, the multiple threads share CPU time,

known as time sharing , and the operating system is responsible for scheduling and allocating

resources to them. This arrangement is practical because most of the time the CPU is idle. It

does nothing, for example, while waiting for the user to enter data.

Multithreading can make your program more responsive and interactive as well as enhance

performance. For example, a good word processor lets you print or save a file while you are

typing. In some cases, multithreaded programs run faster than single-threaded programs even

on single-processor systems. Java provides exceptionally good support for creating and running

threads, and for locking resources to prevent conflicts.

You can create additional threads to run concurrent tasks in the program. In Java, each task

is an instance of the Runnable interface, also called a runnable object. A thread is essentially

an object that facilitates the execution of a task.

32.2.1 Why is multithreading needed? How can multiple threads run simultaneously in a

single-processor system?

32.2.2 What is a runnable object? What is a thread?

32.3 Creating Tasks and Threads

A task class must implement the Runnable interface. A task must be run from a

thread.

Tasks are objects. To create tasks, you have to first define a class for tasks, which implements

the Runnable interface. The Runnable interface is rather simple. All it contains is the run()

method. You need to implement this method to tell the system how your thread is going to run.

A template for developing a task class is shown in Figure 32.2a.

time sharing

task runnable object thread

Point

Check

Point

Key

Runnable interface run() method

32-4 Chapter 32 Multithreading and Parallel Programming

Listing 32.1 TaskThreadDemo.java

1 public class TaskThreadDemo { 2 public static void main(String[] args) { 3 // Create tasks 4 Runnable printA = new PrintChar( 'a' , 100 ); 5 Runnable printB = new PrintChar( 'b' , 100 ); 6 Runnable print100 = new PrintNum( 100 ); 7 8 // Create threads 9 Thread thread1 = new Thread(printA); 10 Thread thread2 = new Thread(printB); 11 Thread thread3 = new Thread(print100); 12 13 // Start threads 14 thread1.start(); 15 thread2.start(); 16 thread3.start(); 17 } 18 } 19 20 // The task for printing a character a specified number of times 21 class PrintChar implements Runnable { 22 private char charToPrint; // The character to print 23 private int times; // The number of times to repeat 24 25 /** Construct a task with a specified character and number of 26 * times to print the character 27 / 28 public PrintChar( char c, int t) { 29 charToPrint = c; 30 times = t; 31 } 32 33 @Override /* Override the run() method to tell the system 34 * what task to perform 35 / 36 public void run() { 37 for ( int i = 0 ; i < times; i++) { 38 System.out.print(charToPrint); 39 } 40 } 41 } 42 43 // The task class for printing numbers from 1 to n for a given n 44 class PrintNum implements Runnable { 45 private int lastNum; 46 47 /* Construct a task for printing 1, 2, ..., n / 48 public PrintNum( int n) { 49 lastNum = n; 50 } 51 52 @Override /* Tell the thread how to run */ 53 public void run() { 54 for ( int i = 1 ; i <= lastNum; i++) { 55 System.out.print( " " + i); 56 } 57 } 58 }

create tasks

create threads

start threads

task class

run

task class

run

32.3 Creating Tasks and Threads 32-

The program creates three tasks (lines 4–6). To run them concurrently, three threads are created

(lines 9–11). The start() method (lines 14–16) is invoked to start a thread that causes the

run() method in the task to be executed. When the run() method completes, the thread

terminates.

Because the first two tasks, printA and printB , have similar functionality, they can

be defined in one task class PrintChar (lines 21–41). The PrintChar class implements

Runnable and overrides the run() method (lines 36–40) with the print-character action. This

class provides a framework for printing any single character a given number of times. The

runnable objects, printA and printB , are instances of the PrintChar class.

The PrintNum class (lines 44–58) implements Runnable and overrides the run() method

(lines 53–57) with the print-number action. This class provides a framework for printing num-

bers from 1 to n , for any integer n. The runnable object print100 is an instance of the class

printNum class.

Note If you don’t see the effect of these three threads running concurrently, increase the number of characters to be printed. For example, change line 4 to Runnable printA = new PrintChar( 'a' , 10000);

Important Note The run() method in a task specifies how to perform the task. This method is automati- cally invoked by the JVM. You should not invoke it. Invoking run() directly merely executes this method in the same thread; no new thread is started.

32.3.1 How do you define a task class? How do you create a thread for a task?

32.3.2 What would happen if you replace the start() method with the run() method in

lines 14–16 in Listing 32.1?

effect of concurrency

run() method

Point

Check

print100.start(); printA.start(); printB.start();

Replaced by print100.run(); printA.run(); printB.run();

32.3.3 What is wrong in the following two programs? Correct the errors.

public class Test implements Runnable { public static void main(String[] args) { new Test(); }

public Test() { Test task = new Test(); new Thread(task).start(); }

public void run() { System.out.println( "test" ); } }

public class Test implements Runnable { public static void main(String[] args) { new Test(); }

public Test() { Thread t = new Thread( this ); t.start(); t.start(); }

public void run() { System.out.println( "test" ); } } (a) (b)

32.4 The Thread Class 32-

This approach is, however, not recommended because it mixes the task and the mechanism of running the task. Separating the task from the thread is a preferred design.

Note The Thread class also contains the stop() , suspend() , and resume() methods. As of Java 2, these methods were deprecated (or outdated ) because they are known to be inherently unsafe. Instead of using the stop() method, you should assign null to a Thread variable to indicate that it has stopped.

You can use the yield() method to temporarily release time for other threads. For example,

suppose that you modify the code in the run() method in lines 53–57 for PrintNum in

Listing 32.1 as follows:

public void run() { for ( int i = 1 ; i <= lastNum; i++) { System.out.print( " " + i); Thread.yield(); } }

Every time a number is printed, the thread of the print100 task is yielded to other threads.

The sleep(long millis) method puts the thread to sleep for a specified time in milli-

seconds to allow other threads to execute. For example, suppose that you modify the code in

lines 53–57 in Listing 32.1 as follows:

public void run() { try { for ( int i = 1 ; i <= lastNum; i++) { System.out.print( " " + i); if (i >= 50 ) Thread.sleep( 1 ); } } catch (InterruptedException ex) { } }

Every time a number ( >= 50 ) is printed, the thread of the print100 task is put to sleep for

1 millisecond.

The sleep method may throw an InterruptedException , which is a checked exception.

Such an exception may occur when a sleeping thread’s interrupt() method is called. The

interrupt() method is very rarely invoked on a thread, so an InterruptedException is

unlikely to occur. But since Java forces you to catch checked exceptions, you have to put it in

a try-catch block. If a sleep method is invoked in a loop, you should wrap the loop in a

try-catch block, as shown in (a) below. If the loop is outside the try-catch block, as

shown in (b), the thread may continue to execute even though it is being interrupted.

deprecated method

yield()

sleep(long)

InterruptedException

public void run() { try { while (...) { ... Thread.sleep( 1000 ); } } catch (InterruptedException ex) { ex.printStackTrace(); } }

public void run() { while (...) { try { ... Thread.sleep(sleepTime); } catch (InterruptedException ex) { ex.printStackTrace(); } } } (a) Correct (b) Incorrect

32-8 Chapter 32 Multithreading and Parallel Programming

You can use the join() method to force one thread to wait for another thread to finish. For

example, suppose that you modify the code in lines 53–57 in Listing 32.1 as follows:

join()

Thread print

Wait for thread to finish

Thread thread

thread4 finished

public void run() { Thread thread4 = new Thread( new PrintChar( 'c', 40 )); thread4.start(); try { for ( int i = 1 ; i <= lastNum; i++) { System.out.print ( " " + i); if (i == 50 ) thread4.join(); } } catch (InterruptedException ex) { } }

thread4.join()

A new thread4 is created and it prints character c 40 times. The numbers from 50 to 100

are printed after thread thread4 is finished.

Java assigns every thread a priority. By default, a thread inherits the priority of the thread

that spawned it. You can increase or decrease the priority of any thread by using the

setPriority 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 , respec-

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

This is called round-robin scheduling. For example, suppose that you insert the following code

in line 16 in Listing 32.1:

thread3.setPriority(Thread.MAX_PRIORITY);

The thread for the print100 task will be finished first.

Tip The priority numbers may be changed in a future version of Java. To minimize the impact of any changes, use the constants in the Thread class to specify thread priorities.

Tip A thread may never get a chance to run if there is always a higher priority thread running or a same-priority thread that never yields. This situation is known as contention or starvation. To avoid contention, the thread with higher priority must periodically invoke the sleep or yield method to give a thread with a lower or the same priority a chance to run.

32.4.1 Which of the following methods are instance methods in java.lang.Thread?

Which method may throw an InterruptedException? Which of them are

deprecated in Java?

run , start , stop , suspend , resume , sleep , interrupt , yield , join

32.4.2 If a loop contains a method that throws an InterruptedException , why should

the loop be placed inside a try-catch block?

32.4.3 How do you set a priority for a thread? What is the default priority?

setPriority(int)

round-robin scheduling

contention or starvation

Point

Check

32-10 Chapter 32 Multithreading and Parallel Programming

42 // Create a scene and place it in the stage 43 Scene scene = new Scene(pane, 200 , 50 ); 44 primaryStage.setTitle( "FlashText" ); // Set the stage title 45 primaryStage.setScene(scene); // Place the scene in the stage 46 primaryStage.show(); // Display the stage 47 } 48 }

The program creates a Runnable object in an anonymous inner class (lines 17–40). This object

is started in line 40 and runs continuously to change the text in the label. It sets a text in the

label if the label is blank (line 23) and sets its text blank (line 25) if the label has a text. The

text is set and unset to simulate a flashing effect.

JavaFX GUI is run from the JavaFX application thread. The flashing control is run from a

separate thread. The code in a nonapplication thread cannot update GUI in the application

thread. To update the text in the label, a new Runnable object is created in lines 27–32. Invok-

ing Platform.runLater(Runnable r) tells the system to run this Runnable object in the

application thread.

The anonymous inner classes in this program can be simplifed using lambda expressions

as follows:

new Thread(() -> { // lambda expression try { while ( true ) { if (lblText.getText().trim().length() == 0 ) text = "Welcome" ; else text = "" ; Platform.runLater(() -> lblText.setText(text)); // lambda exp

Thread.sleep( 200 ); } } catch (InterruptedException ex) { } }).start();

32.5.1 What causes the text to flash? 32.5.2 Is an instance of FlashText a runnable object? 32.5.3 What is the purpose of using Platform.runLater? 32.5.4 Can you replace the code in lines 27–32 using the following code?

Platform.runLater(e -> lblText.setText(text));

32.5.5 What happens if line 34 ( Thread.sleep(200) ) is not used? 32.5.6 There is an issue in Listing 16.9, ListViewDemo. If you press the CTRL key and

select Canada, Demark, and China in this order, you will get an ArrayIndex-

OutBoundsException. What is the reason for this error and how do you fix it?

(Thanks to Henri Heimonen of Finland for contributing this question).

32.6 Thread Pools

A thread pool can be used to execute tasks efficiently.

In Section 32.3, Creating Tasks and Threads, you learned how to define a task class by imple-

menting java.lang.Runnable , and how to create a thread to run a task like this:

Runnable task = new TaskClass(...); new Thread(task).start();

JavaFX application thread

Platform.runLater

Point

Check

Point

Key

32.6 Thread Pools 32-

To create an Executor object, use the static methods in the Executors class, as shown

in Figure 32.8. The newFixedThreadPool(int) method creates a fixed number of threads

in a pool. If a thread completes executing a task, it can be reused to execute another task. If

a thread terminates due to a failure prior to shutdown, a new thread will be created to replace

it if all the threads in the pool are not idle and there are tasks waiting for execution. The

newCachedThreadPool() method creates a new thread if all the threads in the pool are not

idle and there are tasks waiting for execution. A thread in a cached pool will be terminated if

it has not been used for 60 seconds. A cached pool is efficient for many short tasks.

Figure 32.7 The Executor interface executes threads and the ExecutorService subinterface manages threads.

  • shutdown(): void

  • shutdownNow(): List < Runnable >

  • isShutdown(): boolean

  • isTerminated(): boolean

«interface» java.util.concurrent.ExecutorService

  • execute(Runnable object): void

«interface» java.util.concurrent.Executor

Executes the runnable task.

Shuts down the executor, but allows the tasks in the executor to complete. Once shut down, it cannot accept new tasks. Shuts down the executor immediately even though there are unfinished threads in the pool. Returns a list of unfinished tasks. Returns true if the executor has been shut down. Returns true if all tasks in the pool are terminated.

This approach is convenient for a single task execution, but it is not efficient for a large

number of tasks because you have to create a thread for each task. Starting a new thread for

each task could limit throughput and cause poor performance. Using a thread pool is an ideal

way to manage the number of tasks executing concurrently. Java provides the Executor

interface for executing tasks in a thread pool and the ExecutorService interface for man-

aging and controlling tasks. ExecutorService is a subinterface of Executor , as shown

in Figure 32.7.

Figure 32.8 The Executors class provides static methods for creating Executor objects.

Creates a thread pool with a fixed number of threads executing concurrently. A thread may be reused to execute another task after its current task is finished. Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.

java.util.concurrent.Executors

+newFixedThreadPool(numberOfThreads: int): ExecutorService

+newCachedThreadPool(): ExecutorService

Listing 32.3 shows how to rewrite Listing 32.1 using a thread pool.

Listing 32.3 ExecutorDemo.java

1 import java.util.concurrent.*; 2 3 public class ExecutorDemo {

32.7 Thread Synchronization 32-

Figure 32.9 AccountWithoutSync contains an instance of Account and 100 threads of AddAPennyTask.

100 1 AddAPennyTask

+run(): void

AccountWithoutSync

-account: Account

+main(args: String[]): void

1 1 Account

+getBalance(): int +deposit(amount: int): void

-balance: int

«interface» java.lang.Runnable

Listing 32.4 AccountWithoutSync.java

1 import java.util.concurrent.*; 2 3 public class AccountWithoutSync { 4 private static Account account = new Account(); 5 6 public static void main(String[] args) { 7 ExecutorService executor = Executors.newCachedThreadPool(); 8 9 // Create and launch 100 threads 10 for ( int i = 0 ; i < 100 ; i++) { 11 executor.execute( new AddAPennyTask()); 12 } 13 14 executor.shutdown(); 15 16 // Wait until all tasks are finished 17 while (!executor.isTerminated()) { 18 } 19 20 System.out.println( "What is balance? " + account.getBalance()); 21 } 22 23 // A thread for adding a penny to the account 24 private static class AddAPennyTask implements Runnable { 25 public void run() { 26 account.deposit( 1 ); 27 } 28 } 29 30 // An inner class for account 31 private static class Account { 32 private int balance = 0 ; 33 34 public int getBalance() { 35 return balance; 36 } 37 38 public void deposit( int amount) { 39 int newBalance = balance + amount; 40 41 // This delay is deliberately added to magnify the

create executor

submit task

shut down executor

wait for all tasks to terminate

32-14 Chapter 32 Multithreading and Parallel Programming

42 // data-corruption problem and make it easy to see. 43 try { 44 Thread.sleep( 5 ); 45 } 46 catch (InterruptedException ex) { 47 } 48 49 balance = newBalance; 50 } 51 } 52 }

The classes AddAPennyTask and Account in lines 24–51 are inner classes. Line 4 creates

an Account with initial balance 0. Line 11 creates a task to add a penny to the account and

submits the task to the executor. Line 11 is repeated 100 times in lines 10–12. The program

repeatedly checks whether all tasks are completed in lines 17 and 18. The account balance is

displayed in line 20 after all tasks are completed.

The program creates 100 threads executed in a thread pool executor (lines 10–12). The

isTerminated() method (line 17) is used to test whether all the threads in the pool are

terminated.

The balance of the account is initially 0 (line 32). When all the threads are finished, the

balance should be 100 but the output is unpredictable. As can be seen in Figure 32.10, the

answers are wrong in the sample run. This demonstrates the data-corruption problem that

occurs when all the threads have access to the same data source simultaneously.

Figure 32.10 The AccountWithoutSync program causes data inconsistency.

Lines 39–49 could be replaced by one statement:

balance = balance + amount;

It is highly unlikely, although plausible, that the problem can be replicated using this single

statement. The statements in lines 39–49 are deliberately designed to magnify the data-

corruption problem and make it easy to see. If you run the program several times but still

do not see the problem, increase the sleep time in line 44. This will increase the chances for

showing the problem of data inconsistency.

What, then, caused the error in this program? A possible scenario is shown in Figure 32.11.

Figure 32.11 Task 1 and Task 2 both add 1 to the same balance.

Step Balance Task 1 Task 2

1 0 newBalance = balance + 1; (^2 0) newBalance = balance + 1; 3 1 balance = newBalance; (^4 1) balance = newBalance;

32-16 Chapter 32 Multithreading and Parallel Programming

of the code in a method. This block is referred to as a synchronized block. The general form

of a synchronized statement is as follows:

synchronized (expr) { statements; }

The expression expr must evaluate to an object reference. If the object is already locked by

another thread, the thread is blocked until the lock is released. When a lock is obtained on the

object, the statements in the synchronized block are executed and then the lock is released.

Synchronized statements enable you to synchronize part of the code in a method instead

of the entire method. This increases concurrency. You can make Listing 32.4 thread-safe by

placing the statement in line 26 inside a synchronized block:

synchronized (account) { account.deposit( 1 ); }

Note Any synchronized instance method can be converted into a synchronized statement. For example, the following synchronized instance method in (a) is equivalent to (b):

synchronized block

public synchronized void xMethod() { // method body }

public void xMethod() { synchronized ( this ) { // method body } } (a) (b)

32.7.1 Give some examples of possible resource corruption when running multiple

threads. How do you synchronize conflicting threads?

32.7.2 Suppose you place the statement in line 26 of Listing 32.4 inside a synchronized

block to avoid race conditions, as follows:

synchronized ( this ) { account.deposit( 1 ); }

Will it work?

32.8 Synchronization Using Locks

Locks and conditions can be explicitly used to synchronize threads.

Recall that in Listing 32.4, 100 tasks deposit a penny to the same account concurrently, which

causes conflicts. To avoid it, you use the synchronized keyword in the deposit method,

as follows:

public synchronized void deposit( double amount)

A synchronized instance method implicitly acquires a lock on the instance before it executes

the method.

Java enables you to acquire locks explicitly, which give you more control for coordinating

threads. A lock is an instance of the Lock interface, which defines the methods for acquiring and

releasing locks, as shown in Figure 32.13. A lock may also use the newCondition() method

to create any number of Condition objects, which can be used for thread communications.

Point

Check

Point

Key

lock

32.8 Synchronization Using Locks 32-

Figure 32.13 The ReentrantLock class implements the Lock interface to represent a lock.

«interface» java.util.concurrent.locks.Lock

Acquires the lock. Releases the lock. Returns a new Condition instance that is bound to this Lock instance.

java.util.concurrent.locks.ReentrantLock

  • lock(): void
  • unlock(): void
  • newCondition(): Condition

+ReentrantLock() +ReentrantLock(fair: boolean)

Same as ReentrantLock(false). Creates a lock with the given fairness policy. When the fairness is true, the longest-waiting thread will get the lock. Otherwise, there is no particular access order.

ReentrantLock is a concrete implementation of Lock for creating mutually exclusive

locks. You can create a lock with the specified fairness policy. True fairness policies guarantee

that the longest waiting thread will obtain the lock first. False fairness policies grant a lock

to a waiting thread arbitrarily. Programs using fair locks accessed by many threads may have

poorer overall performance than those using the default setting, but they have smaller variances

in times to obtain locks and prevent starvation.

Listing 32.5 revises the program in Listing 32.7 to synchronize the account modification

using explicit locks.

Listing 32.5 AccountWithSyncUsingLock.java

1 import java.util.concurrent.; 2 import java.util.concurrent.locks.; 3 4 public class AccountWithSyncUsingLock { 5 private static Account account = new Account(); 6 7 public static void main(String[] args) { 8 ExecutorService executor = Executors.newCachedThreadPool(); 9 10 // Create and launch 100 threads 11 for ( int i = 0 ; i < 100 ; i++) { 12 executor.execute( new AddAPennyTask()); 13 } 14 15 executor.shutdown(); 16 17 // Wait until all tasks are finished 18 while (!executor.isTerminated()) { 19 } 20 21 System.out.println( "What is balance? " + account.getBalance()); 22 } 23 24 // A thread for adding a penny to the account 25 public static class AddAPennyTask implements Runnable { 26 public void run() { 27 account.deposit( 1 ); 28 } 29 } 30

fairness policy

package for locks

32.9 Cooperation among Threads 32-

Let us use an example to demonstrate thread communications. Suppose you create and

launch two tasks: one that deposits into an account, and one that withdraws from the same

account. The withdraw task has to wait if the amount to be withdrawn is more than the current

balance. Whenever new funds are deposited into the account, the deposit task notifies the

withdraw thread to resume. If the amount is still not enough for a withdrawal, the withdraw

thread has to continue to wait for a new deposit.

To synchronize the operations, use a lock with a condition: newDeposit (i.e., new deposit

added to the account). If the balance is less than the amount to be withdrawn, the withdraw task

will wait for the newDeposit condition. When the deposit task adds money to the account,

the task signals the waiting withdraw task to try again. The interaction between the two tasks

is shown in Figure 32.15.

thread cooperation example

Figure 32.14 The Condition interface defines the methods for performing

synchronization.

«interface» java.util.concurrent.Condition

  • await(): void
  • signal(): void
  • signalAll(): Condition

Causes the current thread to wait until the condition is signaled. Wakes up one waiting thread. Wakes up all waiting threads.

Figure 32.15 The condition newDeposit is used for communications between the two

threads.

while (balance < withdrawAmount) newDeposit.await();

Withdraw Task

balance – = withdrawAmount

lock.unlock();

Deposit Task

lock.lock();

newDeposit.signalAll();

balance += depositAmount

lock.unlock();

lock.lock();

You create a condition from a Lock object. To use a condition, you have to first obtain a

lock. The await() method causes the thread to wait and automatically releases the lock on the

condition. Once the condition is right, the thread reacquires the lock and continues executing.

Assume the initial balance is 0 and the amount to deposit and withdraw are randomly gen-

erated. Listing 32.6 gives the program. A sample run of the program is shown in Figure 32.16.

Figure 32.16 The withdraw task waits if there are not sufficient funds to withdraw.

32-20 Chapter 32 Multithreading and Parallel Programming

Listing 32.6 ThreadCooperation.java

1 import java.util.concurrent.; 2 import java.util.concurrent.locks.; 3 4 public class ThreadCooperation { 5 private static Account account = new Account(); 6 7 public static void main(String[] args) { 8 // Create a thread pool with two threads 9 ExecutorService executor = Executors.newFixedThreadPool( 2 ); 10 executor.execute( new DepositTask()); 11 executor.execute( new WithdrawTask()); 12 executor.shutdown(); 13 14 System.out.println( "Thread 1\t\tThread 2\t\tBalance" ); 15 } 16 17 public static class DepositTask implements Runnable { 18 @Override // Keep adding an amount to the account 19 public void run() { 20 try { // Purposely delay it to let the withdraw method proceed 21 while ( true ) { 22 account.deposit(( int )(Math.random() * 10 ) + 1 ); 23 Thread.sleep( 1000 ); 24 } 25 } 26 catch (InterruptedException ex) { 27 ex.printStackTrace(); 28 } 29 } 30 } 31 32 public static class WithdrawTask implements Runnable { 33 @Override // Keep subtracting an amount from the account 34 public void run() { 35 while ( true ) { 36 account.withdraw(( int )(Math.random() * 10 ) + 1 ); 37 } 38 } 39 } 40 41 // An inner class for account 42 private static class Account { 43 // Create a new lock 44 private static Lock lock = new ReentrantLock(); 45 46 // Create a condition 47 private static Condition newDeposit = lock.newCondition(); 48 49 private int balance = 0 ; 50 51 public int getBalance() { 52 return balance; 53 } 54 55 public void withdraw( int amount) { 56 lock.lock(); // Acquire the lock 57 try { 58 while (balance < amount) {

create a lock

create a condition

acquire the lock

create two threads