In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
What this article shares with you is about what is the basis of Java and Scala concurrency in JVM. The editor thinks it is very practical, so I share it with you to learn. I hope you can get something after reading this article.
Processor speed has been developing rapidly for decades and has come to an end at the turn of the century. Since then, processor manufacturers have improved chip performance more by adding cores than by increasing clock rates. Multicore systems are now the standard for everything from mobile phones to enterprise servers, and this trend is likely to continue and accelerate. Developers increasingly need to support multiple cores in their application code in order to meet performance requirements.
In this series of articles, you will learn about some new approaches to concurrent programming for Java and Scala languages, including how Java combines ideas that have been explored in Scala and other JVM-based languages. The * article will introduce some background and help you get a panoramic view of concurrent programming on JVM by introducing some of the technologies of Java 7 and Scala. You will learn how to use the Java ExecutorService and ForkJoinPool classes to simplify concurrent programming. You'll also learn about some basic Scala features that extend concurrent programming options to pure Java beyond what you already have. In the process, you will see how different approaches affect the performance of concurrent programming. Future articles will cover concurrency improvements and some extensions in Java 8, including the Akka toolkit for performing extensible Java and Scala programming.
Java concurrency support
In the early days of the Java platform, concurrency support was one of its features, and the implementation of threading and synchronization gave it an advantage over other competitive languages. Scala is based on Java and runs on JVM, with direct access to all Java runtimes (including all concurrency support). So before I analyze the Scala features, I'll first take a quick look at what the Java language already provides.
Java Thread Foundation
It is very easy to create and use threads during Java programming. They are represented by the java.lang.Thread class, and the code to be executed by the thread is in the form of a java.lang.Runnable instance. If necessary, you can create a large number of threads in your application, and you can even create thousands of threads. When there are multiple cores, JVM uses them to execute multiple threads concurrently; threads beyond the number of cores share those cores.
Java 5: the turning point of concurrency
Java includes support for threading and synchronization from the very beginning. However, the initial specification for sharing data between threads is not perfect, which brings about a major change in the Java language update of Java 5 (JSR-133). Java Language Specification for Java 5 corrects and normalizes synchronized and volatile operations. The specification also specifies how immutable objects use multithreading. Basically, immutable objects are always thread-safe as long as references to "escape" are not allowed when the constructor is executed. Previously, interactions between threads usually required the use of blocked synchronized operations These changes support non-blocking coordination between threads using volatile. As a result, new concurrent collection classes have been added to Java 5 to support non-blocking operations-a significant improvement over earlier thread-safe approaches that only support blocking.
The coordination of thread operations is difficult to understand. As long as everything is consistent from a program's point of view, the Java compiler and JVM will not reorder the operations in your code, which complicates the problem. For example, if two add operations use different variables, the compiler or JVM can install to perform these operations in reverse order as specified, as long as the program does not use the total number of two variables before both operations are completed. The flexibility of this reordering operation helps improve Java performance, but consistency is only allowed to be applied to a single thread. Hardware can also cause threading problems. Modern systems use a variety of cache memory levels, and in general, not all cores in the system can see these caches equally. When one core modifies a value in memory, other cores may not see the change immediately.
Because of these problems, you must explicitly control how threads interact when one thread uses data modified by another thread. Java uses special operations to provide this control, establishing the order in the data views seen by different threads. The basic operation is that the thread uses the synchronized keyword to access an object. When a thread is synchronized on an object, the thread gets exclusive access to a lock unique to the object. If another thread already holds the lock, the thread waiting to acquire the lock must wait or be blocked until the lock is released. When the thread resumes execution within a block of synchronized code, Java ensures that the thread can "see" all data written by other threads that previously held the same lock, but only those threads release data previously written by that lock by leaving their own synchronized lock. This guarantee applies both to the reordering of operations performed by the compiler or JVM and to hardware memory caching. Inside a synchronized block is a stability island in your code where threads can safely execute, interact, and share information in turn.
The use of the volatile keyword on variables provides a slightly weaker form of secure interaction between threads. The synchronized keyword ensures that you can see the storage of other threads when you acquire the lock, and that after you, other threads that acquire the lock will also see your storage. The volatile keyword breaks down this guarantee into two different parts. If a thread writes data to the volatile variable, it will first erase the data it wrote before. If a thread reads the variable, the thread sees not only the value written to the variable, but also all other values written by the writing thread. So reading a volatile variable provides the same memory guarantee as entering a synchronized block, and writing a volatile variable provides the same memory guarantee as leaving a synchronized block. But there is a big difference between the two: reads or writes to volatile variables are never blocked.
Abstract Java concurrency
Synchronization is useful, and many multithreaded applications are developed using only basic synchronized blocks in Java. But coordinating threads can be cumbersome, especially when dealing with many threads and many blocks. It is difficult to ensure that threads interact only in a safe manner and avoid potential deadlocks (two or more threads wait for each other to release the lock before continuing to execute). Support for concurrency without directly dealing with abstractions of threads and locks provides developers with a better way to deal with common use cases.
The java.util.concurrent hierarchy contains collection variants that support concurrent access, wrapper classes for atomic operations, and synchronization primitives. Many of these classes are designed to support non-blocking access, which avoids deadlock problems and implements more efficient threads. These classes make it easier to define and control interactions between threads, but they still face some of the complexities of the basic threading model.
A pair of abstractions in the java.util.concurrent package that support a more separate approach to concurrency: Future interface, Executor, and ExecutorService interface. These related interfaces in turn form the basis of many Scala and Akka extensions that support Java concurrency, so it is worthwhile to learn more about these interfaces and their implementation.
Future is the holder of a value of type T, but the strange thing is that this value is generally not available until after the Future has been created. This value is not obtained until a synchronization operation is performed correctly. The thread that receives the Future can call the method:
Check if the value is available
Wait for the value to become available
Get the value when it is available
If the value is no longer needed, cancel the operation
The specific implementation structure of Future supports different ways of handling asynchronous operations.
An Executor is an abstraction around something that performs a task. This "thing" will eventually be a thread, but the interface hides the details of the thread's processing execution. The applicability of Executor itself is limited, and the ExecutorService subinterface provides an extended method for managing termination and generates an Future for the results of the task. All standard implementations of Executor also implement ExecutorService, so in fact, you can ignore the root interface.
Threads are relatively heavyweight resources, and it makes more sense to reuse them than to allocate and discard them. ExecutorService simplifies worksharing between threads and supports automatic thread reuse for easier programming and higher performance. The ThreadPoolExecutor implementation of ExecutorService manages a pool of threads that perform tasks.
Application of Java concurrency
The practical use of concurrency often involves tasks that require external interactions (with users, storage, or other systems) that are logically independent of your primary processing. Such applications are difficult to boil down to a simple example, so when demonstrating concurrency, people often use simple compute-intensive tasks, such as mathematical computation or sorting. I will use a similar example.
The task is to find the nearest known word to an unknown input, the closest of which is defined by Levenshtein distance: the minimum number of character additions, deletions, or changes required to convert the input to a known word. The code I use is based on an example in the Levenshtein distance article on Wikipedia, which calculates the Levenshtein distance for each known word and returns a * matching value (or if multiple known words have the same distance, the return result is uncertain).
Listing 1 shows the Java code that calculates the Levenshtein distance. The calculation produces a matrix that matches the rows and columns with the size of the two contrasted text, adding 1 to each dimension. To improve efficiency, this implementation uses a pair of arrays of the same size as the target text to represent the continuous rows of the matrix, wrapping them in each loop, because I only need the value of the previous row to calculate the next row.
Listing 1. Levenshtein distance calculation in Java / * Calculate edit distance from targetText to known word. * * @ param word known word * @ param v0 int array of length targetText.length () + 1 * @ param v1 int array of length targetText.length () + 1 * @ return distance * / private int editDistance (String word, int [] v0, int [] v1) {/ / initialize v0 (prior row of distances) as edit distance for empty 'word' for (int I = 0; I
< v0.length; i++) { v0[i] = i; } // calculate updated v0 (current row distances) from the previous row v0 for (int i = 0; i < word.length(); i++) { // first element of v1 = delete (i+1) chars from target to match empty 'word' v1[0] = i + 1; // use formula to fill in the rest of the row for (int j = 0; j < targetText.length(); j++) { int cost = (word.charAt(i) == targetText.charAt(j)) ? 0 : 1; v1[j + 1] = minimum(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost); } // swap v1 (current row) and v0 (previous row) for next iteration int[] hold = v0; v0 = v1; v1 = hold; } // return final value representing best edit distance return v0[targetText.length()]; } 如果有大量已知词汇要与未知的输入进行比较,而且您在一个多核系统上运行,那么您可以使用并发性来加速处理:将已知单词的集合分解为多个块,将每个块作为一个独立任务来处理。通过更改每个块中的单词数量,您可以轻松地更改任务分解的粒度,从而了解它们对总体性能的影响。清单 2 给出了分块计算的 Java 代码,摘自 示例代码 中的 ThreadPoolDistance 类。清单 2 使用一个标准的 ExecutorService,将线程数量设置为可用的处理器数量。 清单 2. 在 Java 中通过多个线程来执行分块的距离计算private final ExecutorService threadPool; private final String[] knownWords; private final int blockSize; public ThreadPoolDistance(String[] words, int block) { threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); knownWords = words; blockSize = block; } public DistancePair bestMatch(String target) { // build a list of tasks for matching to ranges of known words List tasks = new ArrayList(); int size = 0; for (int base = 0; base < knownWords.length; base += size) { size = Math.min(blockSize, knownWords.length - base); tasks.add(new DistanceTask(target, base, size)); } DistancePair best; try { // pass the list of tasks to the executor, getting back list of futures List results = threadPool.invokeAll(tasks); // find the best result, waiting for each future to complete best = DistancePair.WORST_CASE; for (Future future: results) { DistancePair result = future.get(); best = DistancePair.best(best, result); } } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } return best; } /** * Shortest distance task implementation using Callable. */ public class DistanceTask implements Callable { private final String targetText; private final int startOffset; private final int compareCount; public DistanceTask(String target, int offset, int count) { targetText = target; startOffset = offset; compareCount = count; } private int editDistance(String word, int[] v0, int[] v1) { ... } /* (non-Javadoc) * @see java.util.concurrent.Callable#call() */ @Override public DistancePair call() throws Exception { // directly compare distances for comparison words in range int[] v0 = new int[targetText.length() + 1]; int[] v1 = new int[targetText.length() + 1]; int bestIndex = -1; int bestDistance = Integer.MAX_VALUE; boolean single = false; for (int i = 0; i < compareCount; i++) { int distance = editDistance(knownWords[i + startOffset], v0, v1); if (bestDistance >Distance) {bestDistance = distance; bestIndex = I + startOffset; single = true;} else if (bestDistance = = distance) {single = false;}} return single? New DistancePair (bestDistance, knownWords [bestIndex]): new DistancePair (bestDistance);}}
The bestMatch () method in listing 2 constructs a list of DistanceTask distances, which is then passed to ExecutorService. This call to ExecutorService will accept a Collection
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.