CodeGym /Courses /All lectures for EN purposes /Java Concurrency Library

Java Concurrency Library

All lectures for EN purposes
Level 1, Lesson 360
Available

Multithreading in Java

The Java Virtual Machine supports parallel computing . All calculations can be performed in the context of one or more threads. We can easily set up access to the same resource or object for multiple threads, as well as set up a thread to execute a single block of code.

Any developer needs to synchronize work with threads during read and write operations for resources that have multiple threads allocated to them.

It is important that at the time of accessing the resource you have up-to-date data so that another thread can change it and you get the most updated information. Even if we take the example of a bank account, until the money has come to it, you cannot use it, so it is important to always have up-to-date data. Java has special classes for synchronizing and managing threads.

Thread objects

It all starts with the main (main) thread, that is, at least your program already has one running thread. The main thread can create other threads using Callable or Runnable . The creation differs only in the return result, Runnable does not return a result and cannot throw a checked exception. Therefore, you get a good opportunity to build efficient work with files, but this is very dangerous and you need to be careful.

It is also possible to schedule thread execution on a separate CPU core. The system can easily move between threads and execute a specific thread with the right settings: that is, the thread that reads the data is executed first, as soon as we have data, then we pass it to the thread that is responsible for validation, after that we pass it to the thread to execute some then business logic and a new thread write them back. In such a situation, 4 threads are processing data in turn and everything will work faster than one thread. Each such stream is converted to a native OS stream, but how it will be converted depends on the JVM implementation.

The Thread class is used to create and work with threads. It has standard control mechanisms, as well as abstract ones, such as classes and collections from java.util.concurrent .

Thread Synchronization in Java

Communication is provided by sharing access to objects. This is very effective, but at the same time it is very easy to make a mistake when working. Errors come in two cases: thread interference - when another thread interferes with your thread, and memory consistency errors - memory consistency. To solve and prevent these errors, we have different synchronization methods.

Thread synchronization in Java is handled by monitors, this is a high-level mechanism that allows only one thread to execute a block of code protected by the same monitor at a time. The behavior of monitors is considered in terms of locks; one monitor - one lock.

Synchronization has several important points that you need to pay attention to. The first point is mutual exclusion - only one thread can own the monitor, thus synchronization on the monitor implies that once one thread enters a synchronized block protected by the monitor, no other thread can enter the block protected by the monitor. this monitor until the first thread exits the synchronized block. That is, multiple threads cannot access the same synchronized block at the same time.

But synchronization is not only mutual exclusion. Synchronization ensures that data written to memory before or within a synchronized block becomes visible to other threads that are synchronized on the same monitor. After exiting the block, we release the monitor and another thread can grab it and start executing this block of code.

When a new thread captures the monitor, we gain access to and the ability to execute that block of code, and at that point in time the variables will be loaded from main memory. Then we can see all the entries made visible by the previous release of the monitor.

A single read or write of a field is atomic if the field is declared volatile (this matters for long and double) or if all accesses to it are protected by the same lock. Without synchronization, reordering effects are possible: in an incorrectly synchronized program, one thread may see changes made by another thread in an unexpected order.

The effect of mutual exclusion and synchronization of threads, that is, their correct operation is achieved only by entering a synchronized block or method that implicitly acquires a lock, or by explicitly obtaining a lock. We'll talk about it below. Both ways of working affect your memory and it's important not to forget about working with volatile variables.

Volatile fields in Java

The volatile keyword solves the visibility problem. If a thread has written a value to a volatile field, any other thread that later reads this field will see the new value, not a stale copy.

In terms of memory effects, a write to a volatile field is similar to releasing a monitor, and a read is similar to acquiring it. Everything thread A wrote before writing to a volatile field is guaranteed to be visible to thread B if B has read the written value from that field. This relationship is called happens-before.

A single read or write of a volatile field is atomic, including for long and double. Compound operations, however, such as count++ (read, increment, write), are not: another thread can slip in between the steps, and some of the increments get lost. Mutual exclusion is not something volatile provides. To make a compound operation atomic, you need synchronized, Lock, or classes such as AtomicInteger and AtomicLong.

Java Concurrent

If you want to make a super-efficient and multi-threaded application, you must use the classes from the JavaConcurrent library , which are in the java.util.concurrent package .

The library is very voluminous and has different functionality, so let's take a look at what is inside and divide it into some modules:

Java Concurrent

Concurrent Collections is a set of collections for working in a multi-threaded environment. Instead of the basic wrapper Collections.synchronizedList with blocking access to the entire collection, locks are used on data segments or wait-free algorithms are used to read data in parallel.

Queues - non-blocking and blocking queues for working in a multi-threaded environment. Non-blocking queues focus on speed and operation without blocking threads. Blocking queues are suitable for work when you need to “slow down” the Producer or Consumer threads . For example, in a situation where some of the conditions are not met, the queue is empty or full, or there is no free Consumer 'a.

Synchronizers are utility utilities for synchronizing threads. They are a powerful weapon in "parallel" computing.

Executors is a framework for more convenient and easy creation of thread pools, it is easy to set up scheduling of asynchronous tasks with obtaining results.

Locks are many flexible thread synchronization mechanisms compared to the basic synchronized , wait , notify , notifyAll .

Atomics are classes that can support atomic operations on primitives and references.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION