InterruptedException
, it
simply
returns from the run
method after it catches
that
exception. For example, suppose the central message loop in the
SleepMessages
example were in the run
method
of a thread's Runnable
object. Then it might
be modified
as follows to support interrupts:
for (int i = 0; i < importantInfo.length; i++) {
//Pause for 4 seconds
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
//We've been interrupted: no more messages.
return;
}
//Print a message
System.out.println(importantInfo[i]);
}
Thejoin
method allows one thread to wait for the completion of another. Ift
is aThread
object whose thread is currently executing,causes the current thread to pause execution untilt.join();t
's thread terminates. Overloads ofjoin
allow the programmer to specify a waiting period. However, as withsleep
,join
is dependent on the OS for timing, so you should not assume thatjoin
will wait exactly as long as you specify.Like
sleep
,join
responds to an interrupt by exiting with anInterruptedException
.
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.
- Thread Interference describes how errors are introduced when multiple threads access shared data.
- Memory Consistency Errors describes errors that result from inconsistent views of shared memory.
- Synchronized Methods describes a simple idiom that can effectively prevent thread interference and memory consistency errors.
- Implicit Locks and Synchronization describes a more general synchronization idiom, and describes how synchronization is based on implicit locks.
- Atomic Access talks about the general idea of operations that can't be interfered with by other threads.
public interface Runnable
{
void run();
}
public class MyRunnable implements Runnable
{
public void run()
{
// Task statements go here
. . .
}
}
Runnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
A program to print a time stamp and "Hello World" once a second for ten seconds:
Thu Dec 28 23:12:03 PST 2004 Hello, World!
Thu Dec 28 23:12:04 PST 2004 Hello, World!
Thu Dec 28 23:12:05 PST 2004 Hello, World!
Thu Dec 28 23:12:06 PST 2004 Hello, World!
Thu Dec 28 23:12:07 PST 2004 Hello, World!
Thu Dec 28 23:12:08 PST 2004 Hello, World!
Thu Dec 28 23:12:09 PST 2004 Hello, World!
Thu Dec 28 23:12:10 PST 2004 Hello, World!
Thu Dec 28 23:12:11 PST 2004 Hello, World!
Thu Dec 28 23:12:12 PST 2004 Hello, World!
public class GreetingRunnableimplements Runnable
{
public GreetingRunnable(String aGreeting)
{
greeting = aGreeting;
}
public void run()
{
// Task statements go here
. . .
}
// Fields used by the task statements
private String greeting;
}
Thread.sleep(milliseconds)
public void run()
{
try
{
Task statements
}
catch (InterruptedException exception)
{
}
Clean up, if necessary
}
Runnable t = new GreetingRunnable("Hello World");
Thread t = new Thread(r);
t.start();
Thu Dec 28 23:12:03 PST 2004 Hello, World!
Thu Dec 28 23:12:03 PST 2004 Goodbye, World!
Thu Dec 28 23:12:04 PST 2004 Hello, World!
Thu Dec 28 23:12:05 PST 2004 Hello, World!
Thu Dec 28 23:12:04 PST 2004 Goodbye, World!
Thu Dec 28 23:12:05 PST 2004 Goodbye, World!
Thu Dec 28 23:12:06 PST 2004 Hello, World!
Thu Dec 28 23:12:06 PST 2004 Goodbye, World!
Thu Dec 28 23:12:07 PST 2004 Hello, World!
Thu Dec 28 23:12:07 PST 2004 Goodbye, World!
Thu Dec 28 23:12:08 PST 2004 Hello, World!
Thu Dec 28 23:12:08 PST 2004 Goodbye, World!
Thu Dec 28 23:12:09 PST 2004 Hello, World!
Thu Dec 28 23:12:09 PST 2004 Goodbye, World!
Thu Dec 28 23:12:10 PST 2004 Hello, World!
Thu Dec 28 23:12:10 PST 2004 Goodbye, World!
Thu Dec 28 23:12:11 PST 2004 Goodbye, World!
Thu Dec 28 23:12:11 PST 2004 Hello, World!
Thu Dec 28 23:12:12 PST 2004 Goodbye, World!
Thu Dec 28 23:12:12 PST 2004 Hello, World!
r1.run();instead of starting threads?
r2.run();
t.interrupt();
public void run()
{
for (int i = 1;
i <= REPETITIONS&& !Thread.interrupted()
; i++)
{
Do work
}
Clean up
}
public void run()
{
try
{
for (int i = 1; i <= REPETITIONS; i++)
{
Do work
}
}
catch (InterruptedException exception)
{
}
Clean up
}
public class MyRunnable implements RunnableSuppose a thread with this runnable is started and immediately interrupted.
{
public void run()
{
try
{
System.out.println(1);
Thread.sleep(1000);
System.out.println(2);
}
catch (InterruptedException exception)
{
System.out.println(3);
}
System.out.println(4);
}
}
Thread t = new Thread(new MyRunnable());What output is produced?
t.start();
t.interrupt();
public void run()
{
try
{
for (int i = 1; i <= count; i++)
{
account.deposit(amount);
Thread.sleep(DELAY);
}
}
catch (InterruptedException exception)
{
}
}
public void deposit(double amount)
{
System.out.print("Depositing " + amount);
double newBalance = balance + amount;
System.out.println(", new balance is " + newBalance);
balance = newBalance;
}
Depositing 100.0, new balance is 100.0
Withdrawing 100.0, new balance is 0.0
Depositing 100.0, new balance is 100.0
Depositing 100.0, new balance is 200.0
Withdrawing 100.0, new balance is 100.0
. . .
Withdrawing 100.0, new balance is 0.0
Depositing 100.0Withdrawing 100.0, new balance is 100.0, new balance is -100.0
System.out.print("Depositing " + amount);The balance field is still 0, and the newBalance local variable is 100
double newBalance = balance + amount;
System.out.println(", new balance is " + newBalance);The balance is now 100 instead of 0 because the deposit method used the OLD balance
balance = newBalance;
public void deposit(double amount)Race condition can still occur:
{
balance =balance + amount
;
System.out.print("Depositing " + amount + ", new balance is " + balance);
}
balance = the right-hand-side value
Depositing 100.0, new balance is 100.0
Withdrawing 100.0, new balance is 0.0
Depositing 100.0, new balance is 100.0
Withdrawing 100.0, new balance is 0.0
. . .
Withdrawing 100.0, new balance is 400.0
Depositing 100.0, new balance is 500.0
Withdrawing 100.0, new balance is 400.0
Withdrawing 100.0, new balance is 300.0
public class BankAccount
{
public BankAccount()
{
balanceChangeLock = new ReentrantLock();
. . .
}
. . .
private Lock balanceChangeLock;
}
balanceChangeLock.lock();
Code that manipulates the shared resource
balanceChangeLock.unlock();
public void deposit(double amount)
{
balanceChangeLock.lock();
try
{
System.out.print("Depositing " + amount);
double newBalance = balance + amount;
System.out.println(", new balance is " + newBalance);
balance = newBalance;
}
finally
{
balanceChangeLock.unlock();
}
}
public void withdraw(double amount)
{
balanceChangeLock.lock();
try
{
while (balance < amount)
Wait for the balance to grow
. . .
}
finally
{
balanceChangeLock.unlock();
}
}
public class BankAccount
{
public BankAccount()
{
balanceChangeLock = new ReentrantLock();
sufficientFundsCondition = balanceChangeLock.newCondition();
. . .
}
. . .
private Lock balanceChangeLock;
private Condition sufficientFundsCondition;
}
public void withdraw(double amount)
{
balanceChangeLock.lock();
try
{
while (balance < amount)
sufficientFundsCondition.await();
. . .
}
finally
{
balanceChangeLock.unlock();
}
}
sufficientFundsCondition.signalAll();