Skip to content

Commit

Permalink
Created examples
Browse files Browse the repository at this point in the history
  • Loading branch information
Javinator9889 committed Nov 20, 2018
1 parent f953662 commit 61d1b7c
Show file tree
Hide file tree
Showing 5 changed files with 482 additions and 1 deletion.
6 changes: 5 additions & 1 deletion README.md
Expand Up @@ -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
Expand Down
65 changes: 65 additions & 0 deletions 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;
}
}
```
87 changes: 87 additions & 0 deletions 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).<br /><br />
**Advantages**: you will only have the result when the `Future` is done.<br />
**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.<br /><br />
**Advantages**: you do not need to use any other lib, it is a *built-in*.<br />
**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;
}
}
}
```
121 changes: 121 additions & 0 deletions 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<ArgumentParser> consumer, ArgumentParser args)`
+ `setExecutable(Function<ArgumentParser, ?> 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<HashMap<String, Integer>> 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<Map<String, Integer>> 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<Map<String, Integer>> result = new ArrayList(mThirdField.size());
for (Map<String, Integer> 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<String> supplierResult = new AtomicReference<>();
AtomicReference<List<Map<String, Integer>>> 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());
}
}
```

0 comments on commit 61d1b7c

Please sign in to comment.