SE 450 Fall 2001/2002
Week 8 Lecture Notes
Java I/O (java.io.*)
-
Two types:
-
Stream I/O - sequential reading or writing of data
using byte or character streams.
-
Random Access I/O - random access I/O supporting reading and writing on the same
file.
-
A stream is an ordered sequence of data (bytes) (serial channel of communication)
-
with either a source (input stream)
-
or a destination (output stream)
-
2 main classes: InputStream
and OutputStream (both abstract)
-
InputStream Method: read a byte, or an array of
bytes
-
OutputStream Method: write a byte, or an array of
bytes
-
hard to use and not very useful because you have to convert from byte to
what you are interested in.
-
File streams are the lowest classes in the hierarchy that can be instantiated.
-
Example - Reading
and writing matrices
from/to a file.
-
Filter streams place a layer between the real input or output
stream and the user (see
FilterInputStream or
FilterOutputStream)
-
Note the constructor. It takes an input/output
stream as a parameter.
-
Two useful subclasses which allow you to read/write Java primitives through
the use of the decorator pattern.
-
Example - Reading
and writing matrices
from/to a file made a little bit easier.
-
Buffered streams allow an application to read (write) bytes from (to) a stream without
necessarily causing a call to the underlying system for each byte read (write)
- Buffered classes contain no additional methods
-
System.in is a BufferedInputStream
-
System.out is a
PrintStream, a kind of FilterOutputStream
-
PrintStream methods: print, println
(both are overloaded)
-
Example - Reading
and writing matrices
from/to a file using buffering.
-
Important that you call close() method when you are done to free up
system resources.
-
Unicode is a 16-bit (2 byte) encoding of characters.
-
Unicode characters and byte streams do not mix.
-
Solution:
Reader and
Writer classes
(abstract)
-
Filter and Buffered Readers and Writers
- String i/o:
BufferedReader and
PrintWriter
- Connecting Readers and Writers to Files:
FileReader and
FileWriter
-
Constructor is passed a file name
- Example: creating a BufferedReader that is connected
to a file:
BufferedReader b;
try {
b = new BufferedReader(new FileReader("data.txt")); }
catch (FileNotFoundException file_ex) {
System.out.prinln(file_ex.getMessage());
return; }
String s = b.readLine();
...
-
Example - Reading
and writing matrices from/to a file
using a character stream.
Bridging the Gap
-
InputStreamReader and
OutputStreamReader convert from byte to
character streams using encoding (UTF-8, GB2312[Chinese])
-
Use these when you have an InputStream or OutputStream that you need to connect
to a Reader or Writer.
Decorator Pattern - Structural (p. 303 in Jia)
-
Used to "add on" functionality in a layered fashion
that can be mixed and matched.
-
Eliminates the need to implement all of the different
combinations of possible I/O streams.
-
Allows future additions of functionality without
rewriting old code.
-
Examples:
-
Encryption streams, compressed streams.
Threads
-
Single sequential flow of control within a program.
-
Multi-threaded (a.k.a. concurrent) programs have many
threads running simultaneously.
-
A multi-threaded program can run on a
single-processor or multi-processor computer.
-
Java provides language support for multi-threading.
-
The Java runtime environment is multi-threaded.
Multi-Threaded Programming
-
Most conventional programming languages are
single-threaded.
-
Advantages of multi-threading:
-
reactive systems: control systems
-
responsiveness: GUI
-
availability: servers
-
Disadvantages of multi-threading:
-
shared access to objects
-
overhead: thread creation, context switching and
synchronization
-
race hazard can produce unpredictable results
Creating Threads
-
Approach A:
-
Subclass the Thread class.
-
Override run() method.
-
Create new thread with new MyThread()...
-
Call start() method of the new thread
-
Approach B:
-
Implement Runnable interface
-
Implement run() method
-
Create a new thread using new Thread(runnable)
-
Call start() method on the newly created thread

-
Thread States
-
New - thread has been created but has not had run()
method called
-
Runnable - thread can be run by operating system
-
Blocked - thread can not be run by operating system
-
Methods used to control state
-
start() - move thread to runnable state
-
sleep() - tell thread to go to sleep for a
specified time
-
join() - tell thread to wait for another thread to
finish
-
yield() - give up the processor so some other
thread can run
-
interrupt() - tell this thread that you want it to
stop
-
isAlive() - see if a thread is alive
-
isInterrupted() - see if a thread is interrupted
-
There are other methods (stop(), suspend(), resume()) that have been deprecated
in JDK 1.2 because they can cause unpredictable results. They should not be used!!
Thread Priority and Scheduling
-
Java virtual machine implements a very simple
scheduling strategy.
-
Each thread has a priority between MIN_PRIORITY and
MAX_PRIORITY.
-
A live thread may be runnable or blocked.
-
The runnable thread of the highest priority will be selected to run.
-
If there are more than one runnable threads of the same highest priority,
one will be selected arbitrarily. These is no requirement of fairness.
-
A thread of a higher priority will preempt a thread
of a lower priority.
-
A thread that is currently running will relinquish the control whenone of the following happens:
-
It yields, i.e., yield() is invoked.
-
It becomes blocked.
-
A thread with a higher priority becomes runnable.
-
Its time-slice has expired.
Safety and Liveness
-
Saftey means that certian conditions should hold
throughout the lifetime of a program (aka invariants).
-
Liveness means that there is always some useful work
being done throughout the life of the program.
-
As safety goes up, liveness goes down as well as the
other way around.
-
Multi-threaded programming
forces us to balance the
two factors.
Safety Problems
- What happens if
a
thread
is interrupted in the middleofdoing somethingtoan
object??
-
The object could end up in an inconsistent state.
-
Example: a
bank account
public class Account { //...
public boolean withdraw(long amount)
{
if (amount <= balance) {
long newbalance = balance - amount;
balance =
newbalance; return
true; }
else return
false;
}
private long balance;
}
- Can you find a path of execution that could cause a problem?? How about 2
withdrawals that are done at almost the same time on 2 different threads.
- How do we solve this?? Exclusion!!!
- Exclusion (thread safety) assures us that at most one thread will be
executing the "critical section" of a method at one time.
- These methods are called "atomic operations".
- A safer version of the bank account using exclusion. public class Account {
// ...
public synchronized boolean withdraw(long amount) {
if (amount <= balance) {
long newbalance = balance - amount;
balance = newbalance;
return true;
} else
return false;
}
private long balance;
}
or
public class Account {
// ...
private Object lock = new Object();
public boolean withdraw(long amount) {
synchronized(lock){
if (amount <= balance) {
long newbalance = balance - amount;
balance = newbalance;
return true;
} else
return false;
}
}
private long balance;
}
-
The synchronized keyword locks on the current (ie this) object and prevents more than one thread from
acting on the object.
-
Locks can be shared by many methods
public class A{
synchronized void m1(){...}
synchronized void m2(){...}
void m3(){...}
-
Only one thread can be executing either m1 or m2 on
any instance of A but any number of thread can be executing m3.
- Another way of using locks:
public class Account {
// ...
public boolean withdraw(long amount) {
synchronized (this) {
if (amount <= balance) {
long newbalance = balance - amount;
balance = newbalance;
return true;
} else
return false;
}
}
}
-
Examples: Bounded queue
and safe bounded queue
Cooperation Between Threads
-
Synchronization only deals with exclusion of threads
not cooperation between threads.
-
What's wrong with the thread safe queue above??
Possible loss of information.
-
Threads can cooperate using the wait()/notify()
mechanism of the Object class.
-
Change bounded queue in the following ways:
-
if queue is full, producer waits until it can put
something new into the queue.
-
if queue is empty, consumer waits until there is something in the queue.
-
How do we accomplish this?? Guarded suspension!!!
-
Before method is executed, check the guard.
-
If guard is true, continue execution.
-
If guard is false, wait until it is true.
-
Example - Guarded Bounded Queue
Liveness Problems
-
Livelock - when one or more threads get starved for
CPU time.
-
Deadlock - when all thread stop working because they
are waiting on each other
-
Famous deadlock problem - The dining philosophers.
-
4 philosophers sitting around a table
-
1 fork in between each one
-
You need two forks to eat
-
Deadlock solutions:
-
Resource ordering - make sure they grab the forks
in a certain order
-
Tokens - use one less token than philosophers and force each philosopher to get
a token before grabbing forks.