diff --git a/README.md b/README.md index 7df1539..bc64d54 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,11 @@ After [successfully included the library in your project](#2-installation), you + Setup the `NotifyingThreads`, and include them inside the `ThreadsPooling`. + Call `ThreadsPooling.start()` method for start executing the threads. -You can see some examples at the [test folder](https://github.com/Javinator9889/ThreadingTools/tree/master/threadingtools/src/test/java), in which all those process and lambda usage are specified. +You can see some examples at the [examples folder](https://github.com/Javinator9889/ThreadingTools/tree/master/examples), +in which all those process and lambda usage are specified. + +If you need more information, [read the docs](https://javinator9889.github.io/ThreadingTools/) in + which you will find **every method detailed and explained**. ## 4. Contributing diff --git a/examples/1-CreatingAPooling.md b/examples/1-CreatingAPooling.md new file mode 100644 index 0000000..2e07681 --- /dev/null +++ b/examples/1-CreatingAPooling.md @@ -0,0 +1,65 @@ +# Examples + +## 1. Creating a pooling + + +One of the common usages of **this library** (*[ThreadingTools](https://github.com/Javinator9889/ThreadingTools)*) + is to concurrently run **a predefined number of threads**, but specifying upper limits (for not + running all threads at the same time). + + *ThreadingTools* provides a utility for generating a **pool** of threads that will run + concurrently, `ThreadsPooling`, accessed via `Builder` inner class. + +For creating a new instance, we will use: + + + `builder()`, for obtaining the builder object. + + `build()`, for creating the `ThreadsPooling` class. + +All other methods are detailed and specified at the +[official documentation](https://javinator9889.github.io/ThreadingTools/com/github/javinator9889/threading/pools/ThreadsPooling.Builder.html#method.detail) +which explains everything you need to know for creating a new `ThreadsPooling` class. + +---------------- + +```java +import com.github.javinator9889.threading.pools.ThreadsPooling; + + +public class ExampleClass { + private ThreadsPooling mPooling; + + public ExampleClass() { + // Constructor that initializes the ThreadsPooling object + + // In this example, we will use the number of processor + // for defining the concurrent jobs running. + // Also, the maximum jobs running will be twice number + // of processor. + int numberOfProcessors = getNumberOfProcessors(); + int maxProcessRunning = numberOfProcessor * 2; + // We are going to execute at most 20 threads + int queueCapacity = 20; + // We want threads to terminate when they become idle + // after one second + long keepAliveTime = 1L; + + // We are leaving ThreadFactory and + // RejectedExecutionHandler to their defaults + mPooling = ThreadsPooling.builder() + .withConcurrentThreadsRunning(numberOfProcessors) + .withMaximumPoolSize(maxProcessRunning) + .withQueueCapacity(queueCapacity) + .withKeepAliveInSeconds(keepAliveTime) + .build(); + + // Now we have the ThreadsPooling object created + // We could have add the threads we want to execute + // with the methods: "withThread()" and "withThreads" + } + + // Method for getting the generated ThreadsPooling + public ThreadsPooling getPooling() { + return mPooling; + } +} +``` \ No newline at end of file diff --git a/examples/2-CreatingANotifyingThread.md b/examples/2-CreatingANotifyingThread.md new file mode 100644 index 0000000..b6f657d --- /dev/null +++ b/examples/2-CreatingANotifyingThread.md @@ -0,0 +1,87 @@ +# Examples + +## 2. Creating a NotifyingThread + +As explained at *README*, there are several ways to know when a **thread** has finished its job: + + + **Using** a `Future` object, in which you define *what is going to be executed* and then, + wait for its result (if it has one).

+ **Advantages**: you will only have the result when the `Future` is done.
+ **Disadvantages**: if there is no result, you will not know when the `Future` is done. + + + **Using** a `Thread` object, and call `join()`, blocking your main thread (or *any other + thread where `join()` was called*) for a non-defined time.

+ **Advantages**: you do not need to use any other lib, it is a *built-in*.
+ **Disadvantages**: you dedicate a **hole process** to wait for a thread that maybe do not finish. + +With `NotifyingThread`, all those problems disappear, as: + + + You can **get notified** of the threads *you want to get notified by*. + + You do not have to **actively wait** for a thread to *finish*. + + You can use them as `Future`, because until the result is not set, there is nothing at its + output. + +For taking advantage of all those *functionalities*, you just need to **subscribe you class** +(that must implement `OnThreadCompletedListener`) to the correspondent `NotifyingThread`. If not, + it is just a simple thread 🤷‍ + +------------------ + +```java +import com.github.javinator9889.threading.threads.notifyingthread.*; + +public class NotifyingThreadExampleClass implements OnThreadCompletedListener { + // Default constructor - we do not need anything else + public NotifyingThreadExampleClass() {} + + public void executeAThread() { + // Generate a new NotifyingThread + // We are using a lambda expression for providing the + // Runnable class; it is the same as doing: + // + // new NotifyingThread(new Runnable() { + // @Override + // public void run() { + // System.out.println("Hi, I am a NotifyingThread"); + // } + // }); + NotifyingThread thread = new NotifyingThread(() -> + System.out.println("Hi, I am a NotifyingThread") + ); + + // Subscribe this class (NotifyingThreadExampleClass) + // for getting notified whether the thread has finished + // or it was interrupted by an exception + thread.addOnThreadCompletedListener(this); + + // Setup a custom name for identification + thread.setName("NotifyingThreadExample"); + + // Start executing - this is always necessary + thread.start(); + } + + // Custom implementation for knowing that a thread has finished + // Here, we can wait until a concrete thread has finished for starting + // another job that requires the thread that called "onThreadCompletedListener" + // to finish. + @Override + public void onThreadCompletedListener(Thread thread, @Nullable Throwable exception) { + switch (thread.getName()) { + case "NotifyingThreadExample": + System.out.println("NotifyingThread finished!"); + if (exception != null) { + System.err.println("NotifyingThread threw an exception during execution"); + exception.printStackTrace(); + } else { + System.out.println("NotifyingThread finished without errors"); + anotherThreadThatRequiresNotifyingThreadToFinish().start(); + } + break; + default: + System.err.println("Mmmmm"); + break; + } + } +} +``` \ No newline at end of file diff --git a/examples/3-UsingLambdas.md b/examples/3-UsingLambdas.md new file mode 100644 index 0000000..f1d1e7e --- /dev/null +++ b/examples/3-UsingLambdas.md @@ -0,0 +1,121 @@ +# Examples + +## 3. Using lambdas + +`Thread` does not allow us to **change its runnable** once it has been created, which is not a +problem (is very strange to change that once a thread is created) but can be. + +If you have read the [documentation about NotifyingThread](https://javinator9889.github.io/ThreadingTools/com/github/javinator9889/threading/threads/notifyingthread/NotifyingThread.html), +you have noticed that there are **four methods** for setting an executable *once the class is +created*. Those methods are: + + + `setExecutable(Runnable runnable)` + + `setExecutable(Consumer consumer, ArgumentParser args)` + + `setExecutable(Function function, ArgumentParser args, AtomicReference result)` + + `setExecutable(Supplier supplier, AtomicReference result)` + +Those methods, taking advantage of **lambda expressions**, allows you to directly **refer to a +method** and its arguments on a single line, with the following format: + + class::methodReference + +-------------- + +```java +import com.github.javinator9889.threading.threads.notifyingthread.NotifyingThread; +import com.github.javinator9889.utils.ArgumentParser; + +public class LambdasExampleClass { + private int mFirstField; + private String mSecondField; + private ArrayList> mThirdField; + + // A constructor that initializes the class fields + // All methods used are invented and they have no implementation + public LambdasExampleClass() { + mFirstField = getRandomValue(); + mSecondField = getRandomString(); + mThirdField = generateListOfMaps(); + } + + public void runnableOperation() { + // Operation that takes no arguments and return nothing + System.out.println(mFirstField + mSecondField + mThirdField.toString()); + } + + public void consumerOperation(ArgumentParser args) { + int valueToConsume = args.getInt("value"); + // Do something with the value, for example, assigning + // "mFirstField" to the pow of that two values + mFirstField = Math.pow(valueToConsume, mFirstField); + } + + public String supplierOperation() { + // Do something with the "mSecondField", for example, + // appending current thread name + return mSecondField + Thread.currentThread().getName(); + } + + public List> functionOperation(ArgumentParser args) { + int firstValue = args.getInt("firstParam"); + int secondValue = args.getInt("secondParam"); + String thirdValue = args.getString("thirdParam"); + // Do something with the three params and the field + // "mThirdField", for example, obtaining the keys + // in which the value equals "firstValue" or "secondValue" + // and changing it to the "thirdValue" + List> result = new ArrayList(mThirdField.size()); + for (Map currentVal : mThirdField) { + if (currentVal.containsValue(firstValue)) { + // do the interchange + } + if (currentVal.containsValue(secondValue)) { + // do the interchange + } + } + return result; + } + + public void runFunctionsOnANewThread() { + // First, we will generate a new NotifyingThread + // for each function + NotifyingThread first = new NotifyingThread(); + NotifyingThread second = new NotifyingThread(); + NotifyingThread third = new NotifyingThread(); + NotifyingThread fourth = new NotifyingThread(); + + // Now, we need to create ArgumentParser objects + // for our functions arguments. + ArgumentParser consumerArgs = new ArgumentParser(); + ArgumentParser functionArgs = new ArgumentParser(); + + // In addition, we need to create the AtomicReference + // objects that will store the functions result + AtomicReference supplierResult = new AtomicReference<>(); + AtomicReference>> functionResult = new AtomicReference<>(); + + // Finally, setup the params with their values + consumerArgs.putParam("value", getRandomValue()); + functionArgs.putParam("firstParam", getRandomValue()); + functionArgs.putParam("secondParam", getRandomValue()); + functionArgs.putParam("thirdParam", getRandomString()); + + // Now, use lambdas for setting the executables that are + // going to be executed + first.setExecutable(this::runnableOperation); + second.setExecutable(this::consumerOperation, consumerArgs); + third.setExecutable(this::supplierOperation, supplierResult); + fourth.setExecutable(this::functionOperation, functionArgs, functionResult); + + // Start the threads + first.start(); + second.start(); + third.start(); + fourth.start(); + + // Wait for the threads to finish and work with the results + System.out.println(supplierResult.get()); + System.out.println(functionResult.get()); + } +} +``` \ No newline at end of file diff --git a/examples/4-CombiningThreadsAndPooling.md b/examples/4-CombiningThreadsAndPooling.md new file mode 100644 index 0000000..0898b2f --- /dev/null +++ b/examples/4-CombiningThreadsAndPooling.md @@ -0,0 +1,204 @@ +# Examples + +## 4. Combining `NotifyingThreads` and `ThreadsPooling` + +Once you saw **how `ThreadsPooling`** and **`NotifyingThread`** works, lets combine both. + +This section follows a *real implementation* done at the `test` folder. + +-------------------- + +```java +import com.github.javinator9889.utils.ArgumentParser; +import com.github.javinator9889.threading.threads.notifyingthread.OnThreadCompletedListener; +import com.github.javinator9889.threading.threads.notifyingthread.NotifyingThread; +import com.github.javinator9889.threading.pools.ThreadsPooling; + + +public class CombiningClass implements OnThreadCompletedListener { + private static final String HEAVY_OPERATIONS = "heavy_operations"; + private static final String FIRST_DIVIDE = "first_divide"; + private static final String SECOND_DIVIDE = "second_divide"; + private static final String PRIME_FACTORS = "prime_factors"; + private static final int MAXF = Integer.MAX_VALUE / 20; + private AtomicReference mHeavyOperationResult; + private AtomicReference> mPrimeFactorsResult; + private HeavyLoadClass mHeavyLoadClass; + private ThreadsPooling mThreadsPooling; + + public CombiningClass() { + mHeavyOperationResult = new AtomicReference<>(); + mPrimeFactorsResult = new AtomicReference<>(); + mHeavyLoadClass = new HeavyLoadClass(); + mThreadsPooling = ThreadsPooling.builder() + .withQueueCapacity(10) + .withMaximumPoolSize(20) + .withKeepAliveInSeconds(1) + .build(); + } + + public void runOperations() { + // At this method, we will create and subscribe threads + // to the ThreadsPooling. Also, we will know whether a thread + // has finished its execution + + Random generator = new Random(); + // Create the NotifyingThreads with custom names + NotifyingThread mathOperationsThread = new NotifyingThread(HEAVY_OPERATIONS); + NotifyingThread firstDivideBy29Thread = new NotifyingThread(FIRST_DIVIDE); + NotifyingThread secondDivideBy29Thread = new NotifyingThread(SECOND_DIVIDE); + NotifyingThread getFactorPrimeNumbersThread = new NotifyingThread(PRIME_FACTORS); + + // Create and initialize ArgumentParser classes + // We define also the initial capacity as we know + // how many arguments we are going to store + ArgumentParser divisionArgs = new ArgumentParser(1); + ArgumentParser primeFactorArgs = new ArgumentParser(1); + + divisionArgs.putParam("number", generator.nextLong()); + primeFactorArgs.putParam("number", generator.nextInt(MAXF)); + + // Subscribe this class for listenting all threads completion + mathOperationsThread.addOnThreadCompletedListener(this); + firstDivideBy29Thread.addOnThreadCompletedListener(this); + secondDivideBy29Thread.addOnThreadCompletedListener(this); + getFactorPrimeNumbersThread.addOnThreadCompletedListener(this); + + // Assign to each thread its work, by using lambdas + mathOperationsThread.setExecutable(mHeavyLoadClass::mathOperations, + mHeavyOperationResult); + firstDivideBy29Thread.setExecutable(mHeavyLoadClass::isDivisibleBy29); + secondDivideBy29Thread.setExecutable(mHeavyLoadClass::isDivisibleBy29, + divisionArgs); + getFactorPrimeNumbersThread.setExecutable(mHeavyLoadClass::primeFactors, + primeFactorArgs, + mPrimeFactorsResult); + + // Add the threads to the ThreadsPooling object and start execution + mThreadsPooling.add(mathOperationsThread, + firstDivideBy29Thread, + secondDivideBy29Thread, + getFactorPrimeNumbersThread); + mThreadsPooling.start(); + } + + @Override + public void onThreadCompletedListener(@NotNull Thread thread, @Nullable Throwable exception) { + // First, we will handle the exception + if (exception != null) { + System.err.println("Exception thrown by thread: " + thread.getName()); + exception.printStackTrace(); + } + // Handle finished threads + switch (thread.getName()) { + case FIRST_DIVIDE: + // First division thread has finished its execution + System.out.println("First division finished"); + break; + case SECON_DIVIDE: + // Second divide thread has finished its execution + System.out.println("Second division finished"); + break; + case HEAVY_OPERATIONS: + // Math calculations thread has finished its execution + // We can safely print the result + System.out.println("Heavy operations finished"); + System.out.println("Operations result: " + mHeavyOperationResult.get()); + break; + case PRIME_FACTORS: + // Prime factors thread has finished its execution + // We can safely print the result + System.out.println("Prime factors finished"); + System.out.println("Prime factors:"); + List result = mPrimeFactorsResult.get(); + // First element is a descriptor (in this case) + String firstItem = result.remove(0); + // Print prime factors + System.out.println(firstItem + " | Factors: " + Arrays.toString(result.toArray())); + break; + default: + System.err.println("Non contemplated case"); + break; + } + } + + // An example class that takes too much time to finish its working + public class HeavyLoadClass { + private Random mRandom; + private double mFirstAttribute; + private double mSecondAttribute; + private int mMultiplier; + private int mSPF[] = new int[MAXF]; + + // Default constructor, that puts all the values to a + // random one + public HeavyLoadClass() { + mRandom = new Random(); + mFirstAttribute = mRandom.nextDouble(); + mSecondAttribute = mRandom.nextDouble(); + mMultiplier = mRandom.nextInt(12345678); + } + + public double mathOperations() { + // Do mathematical operations by using the + // class fields + double pow = Math.pow(mFirstAttribute * mMultiplier, mSecondAttribute); + double powTan = Math.tan(pow); + double piPow = Math.pow(powTan, Math.PI); + return piPow / Math.sqrt(1337); + } + + public void isDivisibleBy29() { + // Just checks if a multiplication of the class + // fields is divisible by 29 + long number = (long) Math.floor(mFirstAttribute * mMultiplier * Math.E * 100_000_000); + final long original = number; + while (number / 100 > 0) { + int lastDigit = (int) number % 10; + number /= 10; + number += lastDigit * 3; + } + System.out.println("Is " + original + " divisible by 29?: " + (number % 29 == 0)); + } + + public void isDivisibleBy29(ArgumentParser args) { + // Checks if the given value is divisible by 29 + long number = args.getLong("number"); + final long original = number; + while (number / 100 > 0) { + int lastDigit = (int) number % 10; + number /= 10; + number += lastDigit * 3; + } + System.out.println("Is " + original + " divisible by 29?: " + (number % 29 == 0)); + } + + public List primeFactors(ArgumentParser args) { + // Calculate prime numbers factorization for the given value + // It uses "sieve" method + int number = args.getInt("number"); + List result = new ArrayList<>(); + result.add("Original number: " + String.valueOf(number)); + sieve(); + while (number != 1) { + result.add(String.valueOf(mSPF[number])); + number /= mSPF[number]; + } + return result; + } + + // Method for calculating prime numbers + private void sieve() { + mSPF[1] = 1; + IntStream.range(2, MAXF).parallel().forEach(i -> mSPF[i] = i); + for (int i = 4; i < MAXF; i += 2) + mSPF[i] = 2; + for (int i = 3; i * i < MAXF; ++i) + if (mSPF[i] == i) + for (int j = i * i; j < MAXF; j += i) + if (mSPF[j] == j) + mSPF[j] = i; + } + } +} +``` \ No newline at end of file