Multitasking Using Multithreading: Guided by
Multitasking Using Multithreading: Guided by
Submitted by
CERTIFICATE
This is to certify that the seminar titled <<Nanosensors and their applications>> is successfully presented and submitted by
<< Vikram Purushottam Bhat>> ROLL NO: 16
as the partial fulfillment for the Bachelor degree in Computer as prescribed by the University of Pune in the academic year 2011-12.
Date:
Guide
Head of Department
Acknowledgement
We have to thank a few people who have helped me in preparing this project with utmost regards.
Firstly We would like to thank my project guide Prof. S.N Shelke .He has guided us through this seminar report formulation process with great sincerity & knownledge.
We would like to thank our class teacher for initially guiding us through the format of the report & presentation ,thus giving me the exposure of how to formulate a scientific paper if I do so in future.
All the faculty members for guiding me through the project & helping us to choose the right topic.
I would like to thanks our mates Aniket,Anurag,Deepak,Vipul,Akshay and all others who have helped me.
INDEX
ABSTRACT
SYNOPSIS: Multitasking is method by which application programs or OS switches between various tasks to give user the illusion of parallel execution on single processor. The advantage of multitasking is to prevent any resource of the computer hardware from being ideal when there might be & will be a requirement of it in some other process which is currently in blocked stated. Our intentions is to simulate the multitasking of OS or any other application programs to handle two or more tasks using the time slicing or pre-emptive method for alternating between various tasks. We can use multithreading to divide an application or process into parts that can be handled separately and simultaneously. THREADS:- threads are program codes that is run simultaneously but one at an instant and proper switching between threads is done to make maximum use of computer resources and provide illusion of multiprocessing.
We will provide graphical user interface for self defined program, application like text editor and compressor and show how we can easily switch between different applications giving user the illusion of parallel processing on single processor system.
TEXT EDITOR : Text editor is used to write a new file with data or to edit a text file which already existing
We can provide a graphical interface for the Text editor and other menu and options like open , save , new , exit , cut , copy and paste. We can further divide the operations into threads so that the application can simultaneously with other programs using threading.
COMPRESSOR : Compressor or an encryption - decryption software is a used to store data in form of certain patterns to reduce the data size and also provide security. We can design encryption software using Huffman codes & LZW algorithm. Further we can use multithreading to run on multiple files to provide higher CPU utilization. Similarly show the percentage of compressed file to orginal & the amount of process completed using process bar . And also decrypt the data back to original data .
SELF DEFINED PROGRAMS: We will define some program or jobs which will run and have certain instruction which we will execute simultaneously. CONCLUSION:We intend to learn the mechanism of job scheduling of OS through the development of our project.
INTRODUCTION
Most operating systems (OSes) give the appearance of running several applications simultaneously through a process of allocating a few ms of processor time to each application (and then switching to another). This process is commonly termed timesharing. Multitasking within an application, i.e. multithreading, is nothing more than an extension of this basic idea, whereby processor time allocated to an application is separated out amongst all the threads running within that application. When the operating system switches from running one application to another there is a reasonably costly overhead of saving the programs state, e.g. memory registers, etc. (so that processing can successfully resume again from the same point it was paused at). Within Java this is true to a certain extent, however, swapping between threads within an application normally entails a much lower overhead (in terms of what must be saved, etc.). Why use threads ? Threads are beneficial for the following reasons: Through the use of threads programs can be written that dont go dead on the user, i.e. one thread might attend to the GUI, whilst another thread deals with complex calculations (hence,the user can still interact with the program while it continues to process in the other thread).
Certain programs are easier to write using threads (or offer improved performance), e.g. the server of a client-server setup, should ideally process client connections in a separate thread. Certain tasks can be solved using parallel algorithms (e.g. sorting and merging algorithms). Hence, making effective use of any multi-processor computers thread). Certain programs are easier to write using threads (or offer improved performance), e.g. the server of a client-server setup, should ideally process client connections in a separate thread. Certain tasks can be solved using parallel algorithms (e.g. sorting and merging algorithms). Hence, making effective use of any multi-processor computers.
Different kinds of multithreading:Programs that use threads (i.e. multitask) can be broadly separated into four different categories (in order of increasing complexity): 1. Unrelated threads, which run independently of one another (without any interaction between threads). 2. Related but unsynchronized threads, that operate on some common problem, but do not communicate with one another. 3. Mutually-Exclusive threads, which operate on some common task, but which must modify data in a mutually exclusive manner. 4. Communicating Mutually-Exclusive Threads, that cooperate on some common task, sharing and manipulating information in a mutually exclusive manner. In what follows each of the different forms of multithreading will be explored. However, first we consider possible problems arising from the use of threads.
Different forms of multitasking Within an operating system the multitasking of applications is normally accomplished through one of two broad mechanisms: Preemptive multitasking: Under this paradigm, execution of a concurrent process continues until the currently executing process either finishes or voluntarily suspends itself (i.e. lets other processes be considered). Preemptive multitasking places the burden of fair policing upon the running applications to ensure that other processes are not shut out. Timeslicing/Round robin multitasking: The OS assigns each application a small amount of time, termed a time quantum. When the quantum expires, the application is forced to wait whilst other threads get their chance to run. Fortunately, most modern OSes adopt the timeslicing approach to multitasking. However, some older OSes use preemptive multitasking. As a consequence, a concurrent Java application may run differently on different OSes. Hence, in this respect Java is platform dependent (although it is 99.9% safe to assume timeslicing). When you are programming with threads, understanding the life cycle of thread is very valuable. While a thread is alive, it is in one of several states. By invoking start() method, it doesn?t mean that the thread has access to CPU and start executing straight away. Several factors determine how it will proceed. Different states of a thread are :
this state but before the start() method invocation. At this point, the thread is considered not alive.
Runnable state. A thread first enters runnable state after the invoking of start() method but a thread can return to this state after either running, waiting, sleeping or coming back from blocked state also. On this state a thread is waiting for a turn on the processor.
is currently executing. There are several ways to enter in Runnable state but there is only one way to enter in Running state: the scheduler select a thread from runnable pool.
method completes. If any thread comes on this state that means it cannot ever run again.
5. Blocked - A thread can enter in this state because of waiting the
resources that are hold by another thread. Different states implementing Multiple-Threads are:
As we have seen different states that may be occur with the single thread. A running thread can enter to any non-runnable state, depending on the circumstances. A thread cannot enters directly to the running state from non-runnable state, firstly it goes to runnable state. Now lets understand the some non-runnable states which may be occur handling the multithreads.
Sleeping ? On this state, the thread is still alive but it is not runnable, it might be return to runnable state later, if a particular event occurs. On this state a thread sleeps for a specified amount of time. You can use the method sleep( ) to stop the running state of a thread. static void sleep(long millisecond) throws InterruptedException
Waiting for Notification ? A thread waits for notification from another thread. The thread sends back to runnable state after sending notification from another thread. final void wait(long timeout) throws InterruptedException final void wait(long timeout, int nanos) throws InterruptedException final void wait() throws InterruptedException
Blocked on I/O ? The thread waits for completion of blocking operation. A thread can enter on this state because of waiting I/O
resource. In that case the thread sends back to runnable state after availability of resources.
Blocked for joint completion ? The thread can come on this state because of waiting the completion of another thread.
Blocked for lock acquisition ? The thread can come on this state because of waiting to acquire the lock of an object.
ALGORITHMS
The algorithms are divided into two parts :1. logic for the application 2. division of application into thread that can run without conflicts
Encryption agent :-
Encryption is an important technique by which both data protection & data compression is achieved. There are many techniques for data encryption of which we have used two techniques to do the compression given below:1. Huffmans codes 2. LZW compression A Huffman encoder takes a block of input characters with fixed length and produces a block of output bits of variable length. It is a fixed-to-variable length code. Lempel-Ziv, on the other hand, is a variable-to-fixed length code. The design of the Huffman code is optimal (for a fixed blocklength) assuming that the source statistics are known a priori. The Lempel-Ziv code is not designed for any particular source but for a large class of sources. Surprisingly, for any fixed stationary and ergodic source, the Lempel-Ziv algorithm performs just as well as if it was designed for that source. Mainly for this reason, the Lempel-Ziv code is the most widely used technique for lossless file compression.
Huffman Coding:The basic idea in Huffman coding is to assign short codewords to those input blocks with high probabilities and long codewords to those with low probabilities. This concept is similar to that of the Morse code. A Huffman code is designed by merging together the two least probable characters, and repeating this process until there is only one character remaining. A code tree is thus generated and the Huffman code is obtained from the labeling of the code tree. An example of how this is done is shown below.
It does not matter how the characters are arranged. I have arranged it above so that the final code tree looks nice and neat. It does not matter how the final code tree are labeled (with 0s and 1s). I chose to label the upper branches with 0s and the lower branches with 1s. There may be cases where there is a tie for the two least probable characters. In such cases, any tie-breaking procedure is acceptable. Huffman codes are not unique. Huffman codes are optimal in the sense that no other lossless fixed-tovariable length code has a lower average rate. The rate of the above code is 2.94 bits/character. The entropy lower bound is 2.88 bits/character.
So instead of calculating of probability of occurrences of a particular word we calculate the frequency occurrences of each present variable & just store
them in a table. We then construct the tree using frequency of occurrences rather than probability & generate code according to the tree structure.
LZW Compression:LZW Compression is a dictionary based lossless compression technique used to store previous occurrences of string of data in a dictionary & output the index of the string to the output file using the no of bits which are dependent on current dictionary size. The most notable feature of this implementation is that the heaviest step in the whole encoding process - the dictionary search - can be done in a fixed amount of byte comparisons (at most 256 of them) regardless of the dictionary size and the length of the strings. Thus, technically speaking, the dictionary search is an O(1) operation, ie. a constant-time operation. This is done without the need for any external data container (all the ancillary data required for this is stored inside the dictionary itself). Some LZW encoding implementations use an external hash table to perform the dictionary searches. Often these implementations suffer from large memory usage requirements and/or slowness (because the search time may depend on the size of the dictionary and/or the size of the strings inside it). The solution presented here never requires more memory than what was presented in the table above, and always performs a dictionary search in (at most) a fixed amount of steps regardless of the dictionary size. Another notable feature is that the memory usage of the dictionary is not dependent on the input. In other words, even if the input produces very long strings in the dictionary, that doesn't make the dictionary require more memory. This is so for both encoding and decoding. The following terms are used in this text: Byte The fundamental input data element, having possible values between 0 and 255. An arbitrary byte will be denoted with B. A specific value of a byte will be denoted by enclosing the value in parentheses, for example (25). String One or more continuous bytes. Prefix All but the last byte in a string. A prefix will be denoted with [prefix], and thus an arbitrary string will be denoted with [prefix]B. A prefix can be empty (which then means that the string has only one byte). An empty prefix is denoted with [empty]. Dictionary An array of strings (ie. each element of the array contains a string). Index The position of a given string in the dictionary. The first index is 0, the next one is 1, etc. An arbitrary index value will be denoted with <index>.
Assignment. The dictionary The dictionary is an array of strings, or in other words, prefix-byte pairs. Each string in the dictionary is unique, ie. no string appears in the dictionary twice. The first 256 elements in the dictionary consist of pairs of empty prefixes and the byte values corresponding to their index in the dictionary. In other words, the first element of the dictionary is [empty](0), the next one is [empty](1) and so on, up to [empty](255). (When optimizing the algorithm the dictionary can be initialized with less entries if the input data uses less than 256 byte values, but for the sake of simplicity we'll assume in this introductory section that all 256 values are used.) All the new strings added to the dictionary will be added to index positions from 256 upwards. Each new string is added to the next unused position in the dictionary. The basic LZW algorithm This is the basic LZW algorithm in pseudocode: 1. Initialize the dictionary (with the first 256 entries).
2. [prefix] [empty] 3. B next byte in the input. 4. Is the string [prefix]B in the dictionary?
Yes:
1. [prefix] [prefix]B
No:
1. Add the string [prefix]B to the dictionary. 2. Output the index of [prefix] to the result. 3. [prefix] B
Resetting the dictionary Obviously the dictionary cannot grow forever (or else we would at some point run out of memory with very large inputs). For this reason we have to set a maximum size for the dictionary. In practice (as we will see in part 3) we should set a maximum bitsize for the index values of the dictionary (which thus automatically limits the maximum size of the dictionary). For example, if we set this maximum bitsize to 16 that means that the maximum number of elements in the dictionary will be 65536. Obviously this maximum bitsize must be larger than the minimum bitsize which, in this introduction, was 8 bits.
What happens when the dictionary gets full? What we have to do is that immediately when the dictionary gets full (ie. a string is stored in the last possible location of the dictionary) we just reset the dictionary and start over (or, in other words, jump to step 1 in the pseudocode above).
LZW Decoding:Again, we assume that all 256 possible byte values are used. If the encoding used less, then the decoding should do the same. (How many values were used when encoding can be calculated from the byte value map created by the encoder.) If the encoder uses a freely specifiable maximum bitsize for the dictionary size then the encoder should write that value to the output so that the decoder can read it and act accordingly. Note that "first byte of the string at <index>" in the pseudocode refers to the decoded string. In other words, the last byte found when recreating the string (which becomes the first byte when writing it to the output). 1. Initialize the dictionary (with the first 256 entries).
2. <index> first index value in the input. 3. Write the string at <index> to the result. 4. <old> <index> 5. <index> next index value in the input. 6. Does <index> exist in the dictionary?
Yes:
1. Write the string at <index> to the result. 2. B first byte of the string at <index> 3. Add <old>B to the dictionary.
No:
1. B first byte of the string at <old> 2. Add <old>B to the dictionary. 3. Write the string for <old>B to the output. 1. <old> <index>
2. If there are indices left in the input, jump to step 5. Decoding with dynamic bit sizes Decoding with dynamic bit sizes is very similar to how it was done in the encoding step. Notice, however, that there's one twist: In the encoding step the maximum dictionary index value after which the bitsize had to be incremented was calculated as the maximum value which could be represented with that many bits (ie. "(1 << bitsize) - 1"). However, in the decoding stage this value must be one less (that is, "(1 << bitsize) - 2") for the decoding to work properly. In other words, when the dictionary reaches this size, the current bitsize must be
incremented and the new maximum size value calculated again with that formula.
Text Editor:The text editor is a simple notepad editor used to provide simple cut , copy , paste ,open, save, delete action. This function can be implemented using the event based model in java using swing , awt user interfaces.
IMPLEMENTATION
The Text Editor & Compressor are different modules that has to run simultaneously in the same program so there should some way to coordinate between each others task -this way is provided by the thread architecture in Java programming. Java programming as threads which are priority pre-emptive ,that is they can be easily disowned of the processor by the incoming process if the process has higher priority ,then the one which is currently running. The priority can be set for each threads running on the machine so that can be used priority pre-emptive way otherwise the threads of same priority have to wait for other thread to complete its execution & then run is instruction . Language used:- Java (JDK 1.6) environment used:- Netbeans
Text Editor :It is mainly based on event model-that is the user does a particular action and the action listener is activated which is off-course a thread which is in wait state for most periods of time waiting in the event queue, when
event occurs the event handler is dispatched by the java scheduler into ready queue & it has high priority so it pre-empts the running thread & runs the code which used to handle the events that have occurred. Each event has a specific event handler. The java library like swing , awt allow us to implement the event based model. libraries to import:javax.swing java.awt new ActionListener() { public void actionPerformed(ActionEvent e) { //perform action according to event generated }
This is action listener which listens to action performed by the user on particular button or any other compenent & calls the actionPerformed method , which executes the instruction which handle that particular event. We also have Abstractaction class which can be extended & the action performed function can be overridden by the code we want it to perform . class SaveAction extends AbstractAction { public SaveAction() {
super("Save", new ImageIcon("images/Save-icon.png")); } // Query user for a filename and attempt to open and write the text // component's content to the file. public void actionPerformed(ActionEvent ev) { //actions written by programmer } }
Encryption Agent:The encryption agent is a file I/O based application which waits for the I/O device that is secondary memory or the disk to load the required stream of data into the memory. This type of applications donot utilize the processor fairly as they are most of times in the blocked state waiting for I/O interrupt to occur to indicate the fulfillment of I/O request. In such a case Multithreading can be very useful as it can then allow the processor to work on some other ready thread of the same application or other application. We create a thread for the compression action & decompression action so that they can simultaneously for multiple files & increase CPU utilization. Thread creation in java:Using Thread Class:class NewThread extends Thread { NewThread() { // Create a new, second thread super("Demo Thread"); System.out.println("Child thread: " + this); start(); // Start the thread } // This is the entry point for the second thread. public void run() { try { for(int i = 5; i > 0; i--) { System.out.println("Child Thread: " + i); Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted."); } System.out.println("Exiting child thread."); } } Using Runnable Interface:-
class NewThread implements Runnable { Thread t; NewThread() { // Create a new, second thread t = new Thread(this, "Demo Thread"); System.out.println("Child thread: " + t); t.start(); // Start the thread } // This is the entry point for the second thread. public void run() { try { for(int i = 5; i > 0; i--) { System.out.println("Child Thread: " + i); Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted."); } System.out.println("Exiting child thread."); } } The Runnable interface provide a medium through which all the functions of thread class can be used without extending the Thread class hence as in java a class can extend only one class so it allows you to extend you a class which is not allowed by Thread class. The run() method is the function which Thread executes & hence the logic for the compression & decompression is written inside this method so that it can multithreaded. So compress two or files at a time just create separate threads for each. File Operations :- Java provides various types of file operating class with byte stream ,char stream & variable data stream. Streams used in this project:
DataOutputStream: Write into a file with any format of data. DataInputStream: Read into a file with any format of data. FileOutputStream: Write into file with byte arrays. FileInputStream: Read a file with blocks of bytes.
Software Testing
There was fair bit of software testing required for the encryption agent as it is a file based application which gives absurd results if there is a small error in the code. We tested the software using the java debugger for fair amount of files to confirm that it is working for sets of files which we have used for testing.
Netbeans provides powerful support for debugging your Java programs 1) The print statement : its the most elementary form of debugging where the value to be observed is printed
2) Netbeans provides other advanced debugging tools such as setting
breakpoints. the breakpoint will cause the program to pause so we can examine what it's up to while it's still running. We don't have to execute the program by hand. We set the breakpoint, start the program in Debug Mode, and wait for the breakpoint to get hit. The next step is to run the program, but we have to run it in a special way to take advantage of the breakpoint. Figure 04 shows how to launch the program in Debug Mode. In Debug Mode, the program will stop on breakpoints. Debug Mode causes the program to run more slowly than non-Debug Mode, but that's a small price to pay for the power and flexibility of debugging. Debug Mode has a negligible effect on most small programs. The next line to be executed is highlighted in green by NetBeans. The value of variable can be observed by simply hovering the mouse over that variable which is extremely convenient and easy to use. 3) We can also use concept of single-stepping. Since the green highlighted line has not yet been executed, we click Debug / Step Over (or press the F8 key) to excecute that line. The green highlight moves to the next line in the program. We can use single-stepping to follow the code path of your program and find logic errors lurking deep within your code. Java platform debugger Architecture : It has 3 layers : Debugger interface (layer 1)
Layer-1: high-level interface for debugging Layer-2: format of information transfer Layer-3: low-level native interface, applies code changes at jvm level.
Short-cut commands for debugging in java F7 step into executes each source line , if it has method call, and source code is available, pointer moves to that method and executes it. otherwise pointer moves to the next line in the file. F8 step over executes each source line without stepping through the individual instructions/commands. F4 run to cursor execute the program from the current line. F5 - continue resumes debugging until it reaches a next breakpoint or exception or until the program terminates normally Advantages of using debugger Easily and quickly find and resolve the problem Understand the flow of your application code is easier
Results: On average .log file get compressed upto 40% of orginal size & normal text files upto 70%. The PDF files & word produce a less compression even worse for LZW compression. They are mostly stored in compressed form as well. Music Files are already compressed hence further compression is not possible
Conclusion
The project was very important for us because we were for the first time making a application using Java programming language so it helped us to learn more of Java & its concepts. It enhanced our programming skill & helped us to develop logical thinking & expertise our coding. It increased our about threads and their use , states, scheduling , event handling using swing. It increased our knowledge about various compression techniques like Huffmans code & LZW compression which can be useful for future development.