📜 ⬆️ ⬇️

java.util.concurrent. Part One: Why and Why?

Part one, in which the meaning of the existence of this API is revealed by a set of words
This article, although not a direct translation, is based on the article by Brian Goetz by Concurrency in JDK 5.0


Threads allow you to use all available logical processors of a modern computer, be it a desktop or server, and thus potentially perform more operations in the same time.

However, the threads were implemented by modern operating systems long before the mass appearance of multi-core processors. Why do we need threads on single processor systems?
')
There are several reasons

1. Responsive graphical user interface. Long-running operations can be performed in a separate thread, while the application is able to handle keyboard and mouse events.
2. The possibility of more efficient use of resources: processor, memory, hard disk and network. While one of the threads is idle, waiting for the completion of the file read operation, the second stream can at that time establish a network connection with the client, and the third process some request.
3. The simplicity of the flow-to-query model and the idea of ​​background system processes. Leaving the details of processor resource allocation to the conscience of the operating system, we are able to concentrate the program on processing the request itself. We can also select certain tasks (for example, garbage collection or the system debugger) into separate streams, and thus “imperceptibly” add new properties to the program.

The first two points are mostly understandable, the third sign for more detail.

In imperative programming, the algorithms invented and designed by man are sequential instructions that convert some data (input) to other (output). Usually, this is just single-threaded algorithms, when a task is simplified to one set of input values ​​at a time and the algorithm is executed until the result of its work is obtained.

However, the algorithm is not created for a single request. If a large amount of input data is required to process, then there is a desire to either reduce the total processing time or increase the processing speed (and thus increase the processing capacity of the processor). The easiest way in this case is to put more single-threaded algorithms and make them work in parallel. It may be necessary to somehow coordinate access to shared resources and coordinate their implementation, but this is usually much simpler than developing a new algorithm that can process more than one data set at a time (such an algorithm must not only be developed, but also proven). This is understood by the simplicity of the flow-to-query model.

I do not know what was born first - the idea of ​​putting several algorithms on a stream, or the idea of ​​using different algorithms in streams and forcing them to interact in order to solve a more general problem. Most likely the first, but the article is not about that. The reality is that an ordinary modern application is designed as a set of objects that interact with each other. Plus, for the above four reasons, there are also several threads that interact with each other.

Each convinced programmer when programming a multi-threaded application uses common for several (possibly all) threads data located in memory. Some data play a key role in coordinating flows, such as the conditions for completing a large file copy operation, others store information on the state of objects that is common for streams, providing shared access to them, such as spreadsheet values ​​or a list of company employees that are displayed on the screen; The calculation of the third data can be the goal of performing multiple streams, for example, a blog commentary or a site visitors counter

The correctness of operations on such data in a multi-threaded environment is a vital property of any application. Basically, this requirement of correctness comes down to the fact that no thread should observe the internally inconsistent state of the shared resource. This is usually achieved by the requirement that all operations on common data are atomic - both access and change.

Since in Java data are objects, the classes of these objects that will handle calls from several threads are subject to the requirement of multi-thread safety. What does it mean? It. Of course, this means that the calls to the object's methods will be safe, which, of course, is correct, but it doesn’t help us to understand what a class thread-safety is.

First, the thread-safe class must behave correctly in a single-threaded environment. If the class is correctly programmed, that is, it meets its specification, this means that any calls to public methods in any order and any operations on public fields will not lead to an incorrect internal state, will not allow to observe the object in the wrong state, will not break any of the class invariants, nor any of its preconditions or postconditions.

Secondly, the thread-safe class must also continue to behave correctly in a multi-threaded environment without additional synchronization from the side of the calling code, that is, the above should also be observed regardless of the sequence in which the operating system decides to alternate instructions of different class methods.

As a result, all changes in the state of an object by different threads must appear to be consistent and have the correct order without additional efforts in the calling code, and the operating system is able to alternate these changes within the limits allowed by the class developer.

Prior to version 5.0, the only means of JDK for imparting thread-safe properties to classes were synchronization of the execution of methods and blocks using the synchronized keyword, and the interaction was carried out using internal object locks and volatile variables. This was not enough for millions of modern programmers, so the most enterprising of them, by the name of Doug Lee, wrote the book Concurrent Programming in Java. Many of the ideas described in the book formed the basis of JSR 166: Concurrency Utilities and, as a result, under the common umbrella java.util.concurrent API, many new high-level tools were added to JDK 5.0 to facilitate the interaction of streams: blocking data structures, various synchronization tools and many other interesting things that I will discuss in the next section.

Source: https://habr.com/ru/post/115056/


All Articles