930 likes | 1.1k Views
Essential Java classes. mainly classes in the java.lang and java.io packages. Contents. Handling errors using exceptions Multi-threading I/O Setting program attributes Accessing system resources. Exceptions.
E N D
Essential Java classes mainly classes in the java.lang and java.io packages
Contents • Handling errors using exceptions • Multi-threading • I/O • Setting program attributes • Accessing system resources
Exceptions • The Java programming language uses exceptions to provide error-handling capabilities for its programs • An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions • When an error occurs within a method, the method creates an object and hands it off to the runtime system • The object, called an exception object, contains information about the error, including its type and the state of the program when the error occurred • Creating an exception object and handing it to the runtime system is called throwing an exception
//Note: This class won't compile by design! import java.io.*; import java.util.Vector; public class ListOfNumbers { private Vector victor; private static final int SIZE = 10; public ListOfNumbers () { victor = new Vector(SIZE); for (int i = 0; i < SIZE; i++) { victor.addElement(new Integer(i)); } } public void writeList() { PrintWriter out = new PrintWriter( new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + victor.elementAt(i)); } out.close(); } }
private Vector victor; private static final int SIZE = 10; PrintWriter out = null; try { System.out.println("Entered try statement"); out = new PrintWriter(new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + victor.elementAt(i)); } } catch and finally statements . . .
Catch exceptions • try { } catch (FileNotFoundException e) { System.err.println("FileNotFoundException: " + e.getMessage()); throw new SampleException(e); } catch (IOException e) { System.err.println("Caught IOException: " + e.getMessage()); }
Finally • finally { if (out != null) { System.out.println("Closing PrintWriter"); out.close(); } else { System.out.println("PrintWriter not open"); } }
Specify exceptions thrown by methods • public void writeList() throws IOException { PrintWriter out = new PrintWriter( new FileWriter("OutFile.txt")); for (int i = 0; i < SIZE; i++) { out.println("Value at: " + i + " = " + victor.elementAt(i)); } out.close(); }
Throw exceptions • public Object pop() throws EmptyStackException { Object obj; if (size == 0) { throw new EmptyStackException(); } obj = objectAt(SIZE - 1); setObjectAt(SIZE - 1, null); size--; return obj; }
Chained exceptions • Relevant constructor and methods • Throwable getCause() • Throwable initCause(Throwable) • Throwable(String, Throwable) • Throwable(Throwable) • try { } catch (IOException e) { throw new SampleException("Other IOException", e); }
Accessing stack trace • catch (Exception cause) { StackTraceElement elements[] = cause.getStackTrace(); for (int i = 0, n = elements.length; i < n; i++) { System.err.println(elements[i].getFileName() + ":" + elements[i].getLineNumber() + ">> " + elements[i].getMethodName() + "()"); } }
Logging API • try { Handler handler = new FileHandler("OutFile.log"); Logger.getLogger("").addHandler(handler); } catch (IOException e) { Logger logger = Logger.getLogger("package.name"); StackTraceElement elements[] = e.getStackTrace(); for (int i = 0; n = elements.length; i < n; i++) { logger.log(Level.WARNING, elements[i].getMethodName()); } }
Contents • Handling errors using exceptions • Multi-threading • I/O • Setting program attributes • Accessing system resources
Thread • A thread — sometimes called an execution context or a lightweight process — is a single sequential flow of control within a program • A thread is similar to a real process in that both have a single sequential flow of control. However, a thread is considered lightweight because it runs within the context of a full-blown program and takes advantage of the resources allocated for that program • As a sequential flow of control, a thread must carve out some resources within a running program. For example, a thread must have its own execution stack and program counter
A timer • import java.util.Timer; // Simple demo that uses java.util.Timer to // schedule a task to execute once 5 seconds have passed import java.util.TimerTask; public class Reminder { Timer timer; public Reminder(int seconds) { timer = new Timer(); timer.schedule(new RemindTask(), seconds*1000); } class RemindTask extends TimerTask { public void run() { System.out.format("Time's up!%n"); timer.cancel(); //Terminate the timer thread } } public static void main(String args[]) { new Reminder(5); System.out.format("Task scheduled.%n"); } }
Steps of implementing timer • Implement a custom subclass of TimerTask. The run method contains the code that performs the task. • Create a thread by instantiating the Timer class. • Instantiate the TimerTask object. • Schedule the timer task for execution. • //Get the Date corresponding to 11:01:00 pm today. Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 1); calendar.set(Calendar.SECOND, 0); Date time = calendar.getTime(); timer = new Timer(); timer.schedule(new RemindTask(), time);
Stopping timer thread • Invoke cancel on the timer. You can do this from anywhere in the program, such as from a timer task's run method. • Make the timer's thread a "daemon" by creating the timer like this: new Timer(true). If the only threads left in the program are daemon threads, the program exits. • After all the timer's scheduled tasks have finished executing, remove all references to the Timer object. Eventually, the timer's thread will terminate. • Invoke the System.exit method, which makes the entire program (and all its threads) exit.
public class ReminderBeep { ... public ReminderBeep(int seconds) { toolkit = Toolkit.getDefaultToolkit(); timer = new Timer(); timer.schedule(new RemindTask(), seconds*1000); } class RemindTask extends TimerTask { public void run() { System.out.format("Time's up!%n"); toolkit.beep(); //timer.cancel(); //Not necessary because //we call System.exit. System.exit(0); //Stops the AWT thread //(and everything else). } } ... }
Periodic task • public class AnnoyingBeep { Toolkit toolkit; Timer timer; public AnnoyingBeep() { toolkit = Toolkit.getDefaultToolkit(); timer = new Timer(); timer.schedule(new RemindTask(), 0, //initial delay 1*1000); //subsequent rate } class RemindTask extends TimerTask { int numWarningBeeps = 3; public void run() { if (numWarningBeeps > 0) { toolkit.beep(); numWarningBeeps--; } System.exit(0); } } }
Implementing a thread • Subclassing Thread class and overriding run method • public class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run() { for (int i = 0; i < 10; i++) { System.out.format("%d %s%n", i, getName()); try { sleep((long)(Math.random() * 1000)); } catch (InterruptedException e) {} } System.out.format("DONE! %s%n", getName()); } }
public class TwoThreadsTest { public static void main (String[] args) { new SimpleThread("Jamaica").start(); new SimpleThread("Fiji").start(); } }
Implement the Runnable interface and its run method and then create a thread with itself as the Thread's target. • public class Clock extends java.applet.Applet implements Runnable { private volatile Thread clockThread = null; public void start() { if (clockThread == null) { clockThread = new Thread(this, "Clock"); clockThread.start(); } } public void run() { Thread myThread = Thread.currentThread(); while (clockThread == myThread) { repaint(); try { Thread.sleep(1000); } catch (InterruptedException e){ } } } public void stop() { clockThread = null; }
Life cycle of a thread • A thread is started by its start() method • A thread enters Not Runnable state when one of the following events occurs: • Its sleep method is invoked. • The thread calls the wait method to wait for a specific condition to be satisifed. • The thread is blocking on I/O • A thread should arrange for its own death by having a run method that terminates naturally • Thread.getState() method returns Thread.State values that include: NEW,RUNNABLE, BLOCKED , WAITING, TIMED_WAITING, TERMINATED • The Thread.isAlive() method returns true if the thread has been started and not stopped
Scheduling • The Java runtime environment supports a very simple, deterministic algorithm called fixed-priority scheduling • This algorithm schedules threads on the basis of their priority relative to other Runnable threads • When a thread is created, it inherits its priority from the thread that created it. • In addition, by using the setPriority method, you can modify a thread's priority at any time after its creation. • Thread priorities are integers that range between MIN_PRIORITY and MAX_PRIORITY (constants defined in the Thread class).
Relinquishing the CPU • Writing CPU-intensive code can have negative repercussions on other threads running in the same process. • In general, try to write well-behaved threads that voluntarily relinquish the CPU periodically and give other threads an opportunity to run. • A thread can voluntarily yield the CPU by calling the yield method, which gives other threads of the same priority a chance to run. If no equal-priority threads are Runnable, yield is ignored.
Synchronizing threads • public 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(number, i); try { sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } } } }
public class Consumer extends Thread { private CubbyHole cubbyhole; private int number; public Consumer(CubbyHole c, int number) { cubbyhole = c; this.number = number; } public void run() { int value = 0; for (int i = 0; i < 10; i++) { value = cubbyhole.get(number); } } }
Locking an object • public class CubbyHole { private int contents; private boolean available = false; public synchronized int get(int who) { //CubbyHole locked by the Producer. ... //CubbyHole unlocked by the Producer } public synchronized void put(int who, int value) { //CubbyHole locked by the Consumer. ... //CubbyHole unlocked by the Consumer. }
Reentrant lock • The same thread can call a synchronized method on an object for which it already holds the lock, thereby reacquiring the lock • The Java runtime environment allows a thread to reacquire a lock because the locks are reentrant • Reentrant locks are important because they eliminate the possibility of a single thread having to wait for a lock that it already holds
notify and wait methods • public synchronized int get() { //Won't work! if (available == true) { available = false; return contents; } } public synchronized void put(int value) { //Won't work! if (available == false) { available = true; contents = value; } }
public synchronized int get() { while (available == false) { try { //Wait for Producer to put value. wait(); } catch (InterruptedException e) { } } available = false; notifyAll();//Notify Producer that value has been retrieved. return contents; } public synchronized void put(int value) { while (available == true) { try { //Wait for Consumer to get value. wait(); } catch (InterruptedException e) { } } contents = value; available = true;. notifyAll();//Notify Consumer that value has been set }
Running the consumer/producer • public class ProducerConsumerTest { public static void main(String[] args) { CubbyHole c = new CubbyHole(); Producer p = new Producer(c, 1); Consumer c = new Consumer(c, 1); p.start(); c.start(); } }
Explicit lock • An explicit lock is more flexible than using the synchronized keyword because the lock can span a few statements in a method or multiple methods in addition to the scopes (block and method) supported by synchronized • To create an explicit lock, instantiate an implementation of the Lock interface, usually ReentrantLock. To grab the lock, invoke the lock method; to release the lock, invoke the unlock method. You should wrap the lock and unlock methods in a try/finally clause • To wait on an explicit lock, create a condition variable (an object that supports the Condition interface) using the Lock.newCondition method. Condition variables provide the methods await — to wait for the condition to be true, and signal and signalAll — to notify all waiting threads that the condition has occurred
import java.util.concurrent.locks.*; public class CubbyHole2 { private int contents; private boolean available = false; private Lock aLock = new ReentrantLock(); private Condition condVar = aLock.newCondition(); public int get(int who) { aLock.lock(); try { while (available == false) { try { condVar.await(); } catch (InterruptedException e) { } } available = false; condVar.signalAll(); } finally { aLock.unlock(); } return contents; }
public void put(int who, int value) { aLock.lock(); try { while (available == true) { try { condVar.await(); } catch (InterruptedException e) { } } contents = value; available = true; System.out.format("Producer %d put: %d%n", who, contents); condVar.signalAll(); } finally { aLock.unlock(); } }
Synchronized data structure • In your programs, you probably will want to take advantage of the java.util.concurrent package's data structures that hide all the synchronization details
import java.util.concurrent.*; public class Producer3 extends Thread { private BlockingQueue cubbyhole; private int number; public Producer3(BlockingQueue c, int num) { cubbyhole = c; number = num; } public void run() { for (int i = 0; i < 10; i++) { try { cubbyhole.put(i); System.out.format("Producer #%d put: %d%n", number, i); sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } } } }
import java.util.concurrent.*; public class ProducerConsumerTest3 { public static void main(String[] args) { ArrayBlockingQueue c = new ArrayBlockingQueue(1); Producer3 p = new Producer3(c, 1); Consumer3 c = new Consumer3(c, 1); p.start(); c.start(); } }
Thread pool • A thread pool is a managed collection of threads that are available to perform tasks. Thread pools usually provide the following: • Improved performance when executing large numbers of tasks as a result of reduced per-task invocation overhead. • A way of bounding the resources, including threads, consumed when executing a collection of tasks • In addition, thread pools relieve you from having to manage the life cycle of threads. • They allow to take advantage of threading, but focus on the tasks that you want the threads to perform instead of thread mechanics
Thread pool • To use thread pools, instantiate an implementation of the ExecutorService interface and hand it a set of tasks. • The choices of configurable thread pool implementations are ThreadPoolExecutor and ScheduledThreadPoolExecutor. • Recommend using the more convenient factory methods of the Executors class listed in the following table. Factory Methods in the Executors Class newFixedThreadPool(int) Creates a fixed-size thread pool. newCachedThreadPool Creates an unbounded thread pool with automatic thread reclamation. newSingleThreadExecutor Creates a single background thread.
public class WorkerThread implements Runnable { private int workerNumber; WorkerThread(int number) { workerNumber = number; } public void run() { for (int i=0;i<=100;i+=20) { //Perform some work... System.out.format("Worker number: %d, percent complete: %d%n", workerNumber, i); try { Thread.sleep((int)(Math.random() * 1000)); } catch (InterruptedException e) { } } } }
import java.util.concurrent.*; public class ThreadPoolTest { public static void main(String[ ] args) { int numWorkers = Integer.parseInt(args[0]); int threadPoolSize = Integer.parseInt(args[1]); ExecutorService tpes = Executors.newFixedThreadPool (threadPoolSize); WorkerThread[ ] workers = new WorkerThread[numWorkers]; for (int i = 0; i < numWorkers; i++) { workers[i] = new WorkerThread(i); tpes.execute(workers[i]); } tpes.shutdown(); } }
import java.util.concurrent.*; public class CallableWorkerThread implements Callable<Integer> { private int workerNumber; CallableWorkerThread(int number) { workerNumber = number; } public Integer call() { for (int i = 0; i <= 100; i += 20) { //Perform some work... System.out.format("Worker number: %d, percent complete: %d%n", workerNumber, i); try { Thread.sleep((int)(Math.random() * 1000)); } catch (InterruptedException e) {} } return(workerNumber); } }
public class ThreadPoolTest2 { public static void main(String[] args) { int numWorkers = Integer.parseInt(args[0]); ExecutorService tpes = Executors.newCachedThreadPool(); CallableWorkerThread workers[] = new CallableWorkerThread[numWorkers]; Future<Integer> futures[] = new Future[numWorkers]; for (int i = 0; i < numWorkers; i++) { workers[i] = new CallableWorkerThread(i); futures[i]=tpes.submit(workers[i]); } for (int i = 0; i < numWorkers; i++) { try { System.out.format("Ending worker: %d%n", futures[i].get()); } catch (Exception e) {} } } }
Contents • Handling errors using exceptions • Multi-threading • I/O • Setting program attributes • Accessing system resources