Java exceptions are derived from a class called Throwable. It has two subclasses: Error, which deals with errors in the Java virtual machine (those errors are not related to the program code and are beyond a programmer's control) and Exception. The class Exception includes standard kinds of exceptions, s.a. ArithmeticException and ArrayIndexOutOfBounds. A programmer may define his/her own exceptions by extending the class Exception.
A block of code which is monitored for exceptions is included between the keywords try and catch. There may be several catch statements for each try, as long as they catch different kinds of exceptions:
try {
for (int i = 0; i < 10; ++i) {
System.out.println(numer[i] + " / " + denom[i] + " = " + numer[i]/denom[i]);
}
}
catch (ArithmeticException e) {
System.out.println("Division by zero!");
}
catch (ArrayIndexOutOfBounds e) {
System.out.println("Can't go out of the array boundaries!");
}
Here for each element of the array denom which is 0, the program will print the message "Division by zero!", and if at least one of the arrays is shorter than 10 elements, the message "Can't go out of the array boundaries!" will be printed.
Instead of using your own message, as in the example above, you can also just pass the exception as a parameter to println, and a standard message for the exception will be printed. You can also invoke a method printStackTrace() on an exception to print out the sequence of method invocations that the exception occured within.
To catch any possible exception, use catch (Throwable e).
You can declare your own exceptions by extending the class Exception.
In order to display your own exception message, you need to override the default toString() method of an exception. You may use an exception to check for certain conditions in your program. For instance, you if a method requires that a parameter is a positive integer, you can throw an exception if it is not. Exceptions are objects, so in order to throw an exception, you need to create a new exception object first.
Click here for an example.
If a method throws an exception which it does not catch, and the exception is not of the class Error or RuntimeException, then the method declaration must specify the exception that it throws, for instance:
public void mymethod(...) throws Exception1, Exception2 {
//body
}
An important advantage of using threads is that they allow the program to utilize time which would otherwise be spent waiting for an I/O device or some other slower process.
Threads are also called light-weight processes (LWP). They provide convenience of multi-processing (i.e. creating multiple processes run separately by the operating system) without the overhead of setting up a new process. Communication between threads and sharing of resources is also easier than that between separate processes.
Java provides classes and methods that allow creating and manipulating threads. While these classes and methods are part of the Java standard, one has to keep in mind that the actual implementation of threads depends on the operating system, and therefore thread behaviour differs from one machine to another for the same version of Java.
Thread()
Thread(String name)
Thread(Runnable r)
Thread(Runnable r, String name)
setName(String name) method. We discuss Runnable below.
void run(). run() can pass control to other methods, in which case if the thread is suspended, and then resume, it will continue from the point at which it has been suspended.
void start(). run() method. If the thread can get CPU time, it becomes running, otherwise it becomes
ready to run.
static void sleep(long milliseconds) throws InterruptedExceptionThread.sleep(100). It suspends the thread in which it is invoked. Because it throws an exception which is not in RuntimeException, it has to be inside a try/catch block.
final void join() throws InterruptedExceptionthr.join() in the end of main we guarantee that main is not going to terminate until thr does.
static void yield(). main method starts executing, and stops when it terminates. If a programmer needs additional threads, there are two ways of creating them:
Thread. The subclass should override the default method run() with the code that specifies behaviour of the threads. Since threads are objects, a new instance of the class is created by the new keyword. To start the tread, we invoke method start() on each instance.
Runnable interface. The class implements the run() method. To create a new thread this way, one has to create an instance of the class, and then create a new thread, passing the instance of Runnable to the thread constructor as an argument.
Thread cannot extend any other class. If a class implements Runnable inteface, it can also extend another class, which gives more options to a programmer.
synchronized keyword
public class Account {
String holderName;
float amount;
public Account(String name, float amt) {
holderName = name;
amount = amt;
}
public void deposit(float amt) {
amount += amt;
}
public void withdraw(float amt) {
if (amt < amount)
amount -= amt;
}
public float checkBalance() {
return amount;
}
}
Here we can imagine two threads accessing an instance of the class Account via the withdraw method, both trying to withdraw exactly the amount currently on the account. First both threads check if there is sufficient amount for the withdrawal, and both get a positive answer. Then one withdraws money, and then the other. The total on the account is negative! There are other possible bad scenarios involving simultaneous deposits and withdrawals.
We can prevent this scenario by "locking" the account so that only one thread at a time can perform deposits or withdrawals. Each object in Java has a monitor associated with it, and this monitor can be locked by invoking a method declared as "syncronized". If the object is locked, then no syncronized methods can access this object until the method that has locked the object terminates, thus releasing the lock. In the following code if one thread performs a deposit or a withdrawal from the account, no other thread can perform deposits or withdrawals. However, checkBalance() can be performed at any time, since it does not need lock for an the account.
public class Account {
String holderName;
float amount;
public Account(String name, float amt) {
holderName = name;
amount = amt;
}
public syncronized void deposit(float amt) {
amount += amt;
}
public syncronized void withdraw(float amt) {
if (amt < amount)
amount -= amt;
}
public float checkBalance() {
return amount;
}
}
Now threads that want to access the account are syncronized on this account, i.e. the account's monitor locks the account when a thread invokes its withdraw or deposit method.
It is also possible to prevent simultaneous access to a block of code as follows:
syncronized(object) {
// statements to be syncronized
}
Note that we need to specify the object on which to syncronize.
When a thread is waiting to execute a syncronized method on an object that's currently locked, the thread is blocked
wait() and notify()put and get method. This solution does not provide the right thread behaviour, because all it guarantees is that the consumer and the producer thread don't access the buffer simultaneously, but it does not specify the order in which they access it, so the producer might put two pieces of data in the buffer before the consumer takes a data out, or a consumer might attempt to get data out of an empty buffer.
The following three methods of class Object allow threads to communicate so that they can coordinate their access to the buffer:
wait() Suspends the current thread until a notify() is invoked in a method of this object.
notify() Notifies a thread waiting on the object, so that it can resume. Only one thread receives notification. If there are several threads waiting, it depends on the system to decide which one will get the notification.
notifyAll()Notify all threads waiting on the object.
Here is a solution of the consumer/producer problem which uses wait() and notify().
setPriority and getPriority methods of threads. A priority of a thread can be reset at any point in a program.
Another problem that one might encounter in multithreaded programming is infinite postponement: a thread never gets to run because other threads with higher priority always get to run first. Again, care must be taken to avoid this problem.