-
The Object-Oriented Programming (OOP) term is a computer programming model that organizes software design around data, or objects, rather than functions and logic. An object can be defined as a data field that has unique attributes and behavior.
SOURCE: TechTarget
-
The Multithreading term means that you have multiple threads of execution inside the same application. A Thread is like a separate CPU executing your application. Thus, a multithreaded application is like an application that has multiple CPUs executing different parts of the code at the same time.
SOURCE: Jenkov.com
-
Objective - Writing a certain amount of text files. Then, counting the accumulated number of lines of the whole files. This objective was implemented in 3 different solutions:
✔️ Once, while NOT using a thread.
✔️ Once, while using a thead.
✔️ Once, while using a thread pool.
Each solution has performed its own functionality right after the files were successfully written.
-
Runtime Analysis - A runtime analysis was successfully done by passing to each function 1,000 written files such that each file has a random number of lines that is up to 10,000. The files had their own allocated package that were written into - called "WrittenFiles". A temporary output was built in order to clarify each function's performance. The following figure shows:
Each runtime output was calculated by using System.nanoTime() command as the following:
1. long start_time_of_func = System.nanoTime(); 2. // Here we put some program or calling a function... 3. long end_time_of_func = System.nanoTime() - start_time_of_func; 4. System.out.println(end_time_of_func);
The whole solutions have counted the same amount of accumulated lines: 125,860. However, the speed of each solution varies:
Solution Speed # of files # of lines for each file NOT using a thread 1009 ms 1,000 RANDOM - Up to 10,000 Using a thread 318 ms 1,000 RANDOM - Up to 10,000 Using a thread pool 154 ms 1,000 RANDOM - Up to 10,000 NOTE: The speed is measured with milliseconds.
To conclude, threads are basically providing us efficiency. Because, we tend to divide some parts of a program and hand it to some thread to do it for us. Therefore, we save time and that program ends faster. First, we accomplished the objective without using any threads, thus, the function that was specified for this solution has performed the whole work by itself. Second, we solved the problem by creating a thread that was responsible to read a file that will be passed for him, thus, this step has saved more time than the previous solution. Third, a thread pool was made and connected into our objective - which means, we managed to solve our problem by dedicating many threads such that each thread receives a certain file to read and provide us a result at the end, thus, for a high number of files, the multithreading idea would handle them files more efficiently.
Feel free to have a glance about the written files content HERE.
-
U.M.L Diagram -
Please note that, in Ex2_1.java:
1. createTextFiles(int, int, int) - This function writes a certain amount of files such that each file has a random number of lines.
2. getNumOfLines(String []) - This function solves the problem without any threads.
3. getNumOfLinesThreads(String []) - This function solves the problem by using a thread.
4. getNumOfLinesThreadPool(String []) - This function solves the problem by using a thread pool.
-
Objective - Unfortunately, in Java, thread pools don't manage tasks according by their priorities. However, it supports priorities for the threads who are responsible to handle the submitted tasks. Thus, this chapter has came to implement the following:.
✔️ Implementing a generic class that represents an asynchronous operation.
✔️ Implementing a custom thread pool that supports the custom tasks to be handled according to their priorities.
Each one of the above implementations were developed by implementing Java's Callable interface & extending ThreadPoolExecutor class.
-
Design Patterns Used - In order to achieve the objective mentioned above, we have implemented various design patterns that contribute in wrapping our solution as a whole. Design patterns are building blocks of a system's architecture which are recurring solution to design problems we face. They tend to identify and specify abstractions that are above the level of single classes and instances. Design Patterns are classified into 3 categories: Creational, Behavioral & Structural. Now, let's introduce the design patterns we used that all together present us our final solution:
-
Thread Pool: The Thread Pool pattern is a design pattern, used in software engineering to organise the processing of a large number of queued tasks through a smaller/limited number of threads. Results can also be queued. When a thread finishes it's task it requests another. If none are available, the thread can wait or terminate. Such pattern was used in our solution to provide a customized thread pool that progresses tasks according to their priorities and not according to its threads.
-
Active Object: The Active Object pattern decouples method execution from method invocation for objects that each reside in their own thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. This pattern stems of the Thread Pool pattern and it was used in our solution to provide multiple tasks to be executed in our custom thread pool.
-
Adapter: The Adapter pattern lets classes work together that couldn't due to incompatible interfaces. For instance, converting an interface of a class into another interface clients. Such pattern was used in our solution to make our thread pool to accept our customized tasks instead of runnables. Thus, Adapter pattern belongs to the Structural category.
-
Factory Method: The Factory Method pattern defines an interface for creating an object of particular abstract type. However, it lets subclasses decide which concrete class to instantiate. Generally, it is used by many modern frameworks and APIs. Such pattern was used in our solution in order to create customized tasks. Thus, Factory Method pattern belongs to the Creational category.
-
Chain Of Responsibility: The Chain Of Responsibility pattern is used to achieve loose coupling in software design where a request from client is passed to a chain of objects to process them. Then the object in the chain will decide themselves who will be processing the request and whether the request is required to be sent to the next object in the chain or not. Such pattern was used in our solution to provide execptions-handling in a lot of functions. Thus, Chain Of Responsibility patterns belongs to the Behavioral category.
To conclude, let's illustrate how the above 5 mentioned patterns are ACTUALLY related into our solution:
Design Pattern Category How was it used Thread Pool Concurrency Providing a customized thread pool that progresses tasks according to their priorities. Active Object Concurrency Providing multiple tasks to be executed in our custom thread pool. Adapter Structural Making our thread pool accept our customized callable tasks instead of runnables. Factory Method Creational Creating a static method that creates our asynchronous tasks. Chain Of Responsibility Behavioral Providing execptions-handling. -
-
U.M.L Diagram -
Clarification:
-
Task is a class that implements Java's Callable interface. It represents our custom asynchronous task.
-
TaskType is an enum that represents the type of an asynchronous task and their priorities.
-
CustomExecutor is a class that extends from Java's ThreadPoolExecutor class. It represents a custom thread pool that progresses the submitted asynchronous tasks according to their priorities.
-
ComparablePriorityAdapter is a class that extends from Java's FutureTask & implements Java's Comparable interface.
-
Tests is a JUNIT5 class that tests the correctness of our complete solution.
-
Other classes were clarrified in Chapter 1.
-

