Παρασκευή 20 Ιανουαρίου 2023

Concurrency with Synchronized keywork and Atomic classes


 
ACHIEVING SYNCHRONIZATION
 FOR A SHARED INSTANCE BETWEEN MULTIPLE THREADS



Lets suppose we have many threads operating simultaneously on an SynchronizationWith instance which increments a counter by 1 

But we don't want 2 or more to operate at the same time

Because this will create inconsistencies on our controlled adding - counter may increase faster and lose a value. 

So, only one thread should have access to read and write at each moment.

The next thread should get the last value and increment/write on it.

And so on.



THREADS AND MAIN MEMORY








WITH SYNCHRONIZED KEYWORD



Class/Instance for incrementing counter

public class SynchronizationWithSynchronizedKeyword {
private int counter;

int getValue() {
return counter;
}

synchronized void incrementBy1() {
counter++;
}
}

- Synchronized keyword will:

    - Stop other threads from accessing (locking)
    - Read the last value from the main memory
    - Increment it by 1
    - Write the new value in shared memory




Testing

@Test
public void test1() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(3);
SynchronizationWithSynchronizedKeyword syncWithKeyword = new SynchronizationWithSynchronizedKeyword();

IntStream.range(0, 1000).forEach(count -> service.execute(syncWithKeyword::incrementBy1));
shutdownAndAwaitTermination(service);

assertEquals(1000, syncWithKeyword.getValue());
}
- Shared instance: syncWithKeyword
- We initiate 1000 Runnable tasks via a 3-Thread pool
- These tasks reference the shared instance's increment method
- To pass test, each task should increase the counter variable by 1, adding totally to 1000.



Helper method to gracefully shutdown the executor threads
private void shutdownAndAwaitTermination(ExecutorService pool) {
// Disable new tasks from being submitted
pool.shutdown();
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) {
// Cancel currently executing tasks forcefully
pool.shutdownNow();
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ex) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
Result: Success





WITH ATOMIC VARIABLE CLASSES



Class/Instance for incrementing counter
public class SynchronizationWithAtomicClasses {
private final AtomicInteger counter = new AtomicInteger(0);

int getValue() {
return counter.get();
}

void incrementBy1() {
counter.incrementAndGet();
}
}
- AtomicInteger's incrementAndGet when called from a thread, will (atomic operation) :

    - Stop other threads from accessing (locking)
    - Read the last value from the main memory
    - Increment it by 1
    - Write the new value in shared memory



Testing

(Same as above)
@Test
public void test2() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(3);
SynchronizationWithAtomicClasses syncWithAtomic = new SynchronizationWithAtomicClasses();

IntStream.range(0, 1000).forEach(count -> service.execute(syncWithAtomic::incrementBy1));
shutdownAndAwaitTermination(service);

assertEquals(1000, syncWithAtomic.getValue());
}
Result: Success



Τρίτη 17 Ιανουαρίου 2023

Ninja Framework (vs Spring)

 





INTRODUCTION


Ninja is a Java web framework that is lightweight and easy to use.

It is a fully-fledged framework for web development, providing amongst others:


DEPENDENCY INJECTION

ROUTING

MVC 

REST

PERSISTENCE

TESTING

METRICS

LOGGING

I18N

HOT-DEPLOYMENT




ANALYSIS /COMPARISON



DEPENDENCY INJECTION


Ninja uses Google Guice for DI, which has some benefits over the Spring Core DI:

  • Spring still relies heavily on XML configuration, meaning that type safety and refactoring are compromised. It is hard to make automated IDE refactorings on XMLs and Java files. Also, types are not checked in XML. On the other hand, Guice DI is done only on Java files.
  • While Spring DI uses String identifiers to resolve dependency implementation conflicts (when a type has many implementations), which is error-prone, Guice uses @BindingAnnotation in order to remove the need for Strings.
  • Guice allows for constructor injection, with setter injection being discouraged, Last one makes it difficult to have all dependencies centralized and under control.
  • Guice permits source code to remain framework-agnostic, and even allows for coexistence with Spring. We don't need to annotate our classes with Spring stereotypes, thus codebase can be portable.


ROUTING


Ninja has built-in mechanism to support an easy and readable routing:

public class Routes implements ApplicationRoutes {

@Override
public void init(Router router) {

router.GET().route("/index").with(ApplicationController::index);
router.GET().route("/home").with(ApplicationController::home);
router.GET().route("/hello").with(ApplicationController::hello);
router.GET().route("/editUser").with(ApplicationController::editUser);
router.GET().route("/createUser").with(ApplicationController::createUser);
router.GET().route("/books").with(ApplicationController::getAllBooks);

}
}




PERSISTENCE


By default, Ninja core provides Hibernate for persistence, including, Hibernate Validator, Hibernate EntityManager and Hibernate C3P0 for connection pool management.

We can configure DB url and credentials for a H2 along with familiar jpa setup, for example:

application.conf


persistence.xml







LIGHTWEIGHT / EASY SETUP


Using the correct maven archetype, the main skeleton and some code are generated:


Being an integrated software stack, Ninja then provides all libraries for most requirements of a web app.

As it uses convention-over-configuration, development speeds up.

A SuperDevMode functionality is provided to make the dev experience better:

Just insert above dependency and run "mvn ninja:run"



MODES AND APPLICATION CONFIG


Application configuration file is like Spring's one:

We also can reference properties within properties:



Depending on the current mode (dev, test, prod) that app runs, it can load relevant configuration:

To enable modes:

When running test cases, by default Test mode is enabled