Concurrent Threads,executors,forkjoin and actors

Source: Internet
Author: User
Tags static class

Original link: Oleg Shelajev translation: Importnew.com-shenggordon
Translation Links: http://www.importnew.com/14506.html


4 styles of Java Concurrent Programming: Threads,executors,forkjoin and actors

This article discusses a variety of methods for parallel processing in Java applications. From managing Java threads to a variety of more solutions, executor services, Forkjoin frameworks, and actor models in computing.

We live in a world where things happen in parallel. Naturally, the programs we write also reflect this feature, which can be executed concurrently. However, the complexity of concurrent programs is far beyond the human brain's ability to handle. By comparison, we are simply weak: we are not born to think about multithreaded routines, evaluate concurrent access to limited resources, and predict where errors or bottlenecks will occur. Faced with these difficulties, mankind has summed up a number of concurrent computing solutions and models. These models emphasize different parts of the problem, and when we implement parallel computing, we can make different choices according to the problem. In this article, I'll use a different code to implement concurrent solutions for the same problem, and then discuss what's good about these scenarios, what are the flaws, and what traps might be waiting for you.

We will describe the following ways of concurrent processing and asynchronous code:

• Bare Thread

Executors and services

Forkjoin frame and parallel flow

Actor Model

In order to be more interesting, I didn't just use some code to illustrate these methods, but I used a common task, so the code in each section is almost equivalent. In addition, the code is only displayed, the initialization code is not written, and they are not product-level software examples. Task

Task: Implement a method that receives a message and a set of strings as parameters that correspond to the query page of a search engine. For each string, this method sends an HTTP request to query the message and returns the first available result, as soon as possible.

If an error occurs, it is possible to throw an exception or return null. I'm just trying to avoid infinite loops in order to wait for results.

Simple description: This time I will not really delve into the details of how multithreading communicates, or delve into the Java memory model. If you are eager to understand these, you can look at my previous article using jcstress to test concurrency. So let's implement concurrency on the JVM in the most direct and central way:

Manually manage the bare threads.

Method 1: Use the "original" Bare thread

Liberate your code, return to nature, and use bare threads. Threads are the most basic unit of concurrency. Java threads are essentially mapped to operating system threads, and each thread object corresponds to a computer's underlying thread. Naturally, the JVM manages the lifetime of threads, and you don't need to focus on thread scheduling as long as you don't need to communicate between threads. Each thread has its own stack space, which occupies a specified part of the JVM process space. The thread's interface is fairly concise, and you only need to provide a runnable, invoke. Start () to begin the calculation. There is no out-of-the-box API to end the thread, you need to implement it yourself by communicating like a Boolean tag.

In the following example, we create a thread for each search engine that is queried. The result of the query is set to Atomicreference, which does not require a lock or other mechanism to ensure that only one write operation occurs. Let's go.

private static string Getfirstresult (string question, list<string> engines) {
 atomicreference<string> result = new atomicreference<> ();
 for (string base:engines) {
   string url = base + question;
   New Thread (()-> {
     result.compareandset (null, Ws.url (URL). get ());
   Start ();
 }
 while (result.get () = null); Wait for some result to appear return
 result.get ();
}

The main advantage of using bare threads is that you are very close to the operating system/hardware model of concurrent computing, and this model is very simple. Multiple threads running through shared memory communication, that's it.

The biggest disadvantage of managing your own threads is that you tend to be overly concerned with the number of threads. Threads are expensive objects, and it takes a lot of memory and time to create them. It's a paradox, too few threads, you can't get good concurrency; too many threads will likely cause memory problems and scheduling becomes more complex.

However, if you need a quick and easy solution, you can definitely use this method without hesitation.

Method 2: Treat executor and Completionservice seriously

Another option is to use APIs to manage a set of threads. Fortunately, the JVM provides us with the ability to executor interfaces. The definition of the executor interface is simple:

Public interface Executor {
 
void execute (Runnable command);
 
}
It hides the details of how to handle runnable. It just says, "developer." You're just a bag of meat, give me a job and I'll take care of it. ”

Even cooler, the executors class provides a set of methods to create a thread pool and executor with a well-defined configuration. We will use Newfixedthreadpool (), which creates a predefined number of threads and does not allow the number of threads to exceed this predefined value. This means that if all threads are used, the submitted commands will be placed in a queue to wait; Of course, this is managed by executor.

On top of it, there is the lifecycle of Executorservice management executor, and Completionservice abstracts out more details as queues for completed tasks. Thanks to this, we don't have to worry about just getting the first result.

A call to the following Service.take () will return only one result.

private static string Getfirstresultexecutors (string question, list<string> engines) {
 executorcompletionservice<string> service = new Executorcompletionservice<string> ( Executors.newfixedthreadpool (4));
 
 for (string base:engines) {
   string url = base + question;
   Service.submit (()-> {return
     ws.url (URL). get ();
   })
   try {return
     service.take ().
   catch (Interruptedexception | Executionexception e) {return
     null;
   }
}


Executor and executor services will be the right choice if you need accurate control of the number of threads generated by the program and their precise behavior. For example, an important question to consider is what policies are needed when all threads are busy doing other things. Increase the number of threads or do not limit the number. Put the task into the queue to wait. What if the queues are full. Increase the queue size indefinitely.

Thanks to the JDK, there are already a number of configuration items that answer these questions and have an intuitive name, such as Executors.newfixedthreadpool (4) above.

The lifecycle of threads and services can also be configured with options so that resources can be shut down at the right time. The only inconvenience is that, for beginners, configuration options can be simpler and more intuitive. However, in terms of concurrent programming, you can hardly find a simpler one.

In short, for large systems, I personally think it is most appropriate to use executor.

Method 3: Through parallel flow, using Forkjoinpool (FJP)

In Java 8, a parallel stream is added, so we have a simple way to handle the collection in parallel. Together with the lambda, it forms a powerful tool for concurrent computing.

If you plan to use this method, then there are a few points to be noted. First, you have to master some concepts of functional programming, which actually have more advantages. Second, it is difficult to know whether a parallel stream actually uses more than one thread, which is determined by the actual implementation of the stream . If you can't control the flow's data source, you can't be sure what it does.

In addition, you need to keep in mind that by default the Forkjoinpool.commonpool () is implemented in parallel. This universal pool is managed by the JVM and is shared by all threads within the JVM process. This simplifies the configuration items so you don't have to worry.

private static string Getfirstresult (string question, list<string> engines) {
 //Get element as soon as it's AV ailable
 optional<string> result = Engines.stream (). Parallel (). Map (Base)-> {
   String url = base + Question;
   return Ws.url (URL). get ();
 }). Findany ();
 return Result.get ();
}
Looking at the example above, we don't care where individual tasks are done and by whom. However, this also means that there may be some stalled tasks in your application that you cannot know. In another article on parallel streaming, I described the problem in detail. And there is an alternative solution, although it is not the most intuitive solution in the world.

Forkjoin is a good framework for writing and pre-configured by people smarter than me. So when I need to write a small program that includes parallel processing, it's my first choice.

Its biggest drawback is that you have to anticipate the complications it may produce. This is hard to do without a thorough understanding of the JVM as a whole. This can only come from experience.

Method 4: Hire a actor

The actor model is a strange addition to the approach we have discussed in this article. There is no actor implementation in the JDK, so you must refer to some libraries that implement the actor.

In short, in the Actor model, you think of everything as a actor. A actor is a computational entity, like the thread in the first example above, that can receive messages from other actor because everything is actor.

When replying to a message, it can send a message to another actor, or create a new actor and interact with it, or just change its internal state.

Quite simple, but this is a very powerful concept. Lifecycle and messaging are managed by your framework, and you just need to specify what the unit of calculation is. In addition, the Actor model emphasizes avoiding the global state, which can bring a lot of convenience. You can apply monitoring strategies, such as free retries, simpler distributed system design, error tolerance, and more.

Here is an example of using Akka actors. Akka actors has a Java interface and is one of the most popular JVM actor libraries. In fact, it also has a Scala interface and is the current default actor library for Scala. Scala used to implement actor internally. Many JVM languages have implemented actor, such as Fantom. These demonstrate that the actor model has been widely accepted and is viewed as a valuable complement to language.

Static class message {String URL;
Message (String URL) {this.url = URL;}}
 Static class Result {String html;
 
Result (String html) {this.html = html;}} Static Class Urlfetcher extends Untypedactor {@Override public void OnReceive (Object message) throws Exception {I
     F (Message instanceof message) {Message work = (message) message;
     String result = Ws.url (Work.url). get ();
   Getsender (). Tell (new result, getself ());
   else {unhandled (message);
 }} static Class Querier extends Untypedactor {private String question;
 Private list<string> engines;
 
 Private atomicreference<string> result; Public Querier (String question, list<string> engines, atomicreference<string> result) {this.question = Q
   uestion;
   This.engines = engines;
 This.result = result; @Override public void OnReceive (Object message) throws Exception {if (message instanceof result) {result.com Pareandset (null, (Result) message). hTML);
   GetContext (). Stop (self ());
       else {for (string base:engines) {String url = base + question;
       Actorref fetcher = This.getcontext (). Actorof (Props.create (Urlfetcher.class), "fetcher-" +base.hashcode ());
       Message m = new message (URL);
     Fetcher.tell (M, Self ()); '}} ' private static string Getfirstresultactors (string question, list<string> engines) {Actorsystem Syste
 m = actorsystem.create ("Search");
 
 atomicreference<string> result = new atomicreference<> ();  Final Actorref q = system.actorof (Props.create (untypedactorfactory) ()-> new Querier (question, engines, result)),
 "Master");
 
 Q.tell (New Object (), Actorref.nosender ());
 while (result.get () = null);
return Result.get (); }

Akka actor uses the Forkjoin framework internally to handle work. The code here is verbose. Don't worry. Most of the code is defined as message class messages and result, and then two different actor:querier are used to organize all the search engines, and urlfetcher is used to get the results from the given URL. There are more lines of code here because I don't want to write a lot of things on the same line. The strength of the actor model comes from the interface of the props object, through which we can define specific selection patterns for actor, custom email addresses, and so on. The result system is configurable and contains only a small number of active pieces. This is a very good sign.

One disadvantage of using the Actor model is that it requires you to avoid the global state , so you must design your application carefully, which may complicate the project migration. At the same time, it has a number of advantages, so it is entirely worthwhile to learn some new paradigms and use new libraries.

More Actor model Principles http://blog.csdn.net/zhongweijian/article/details/7805346

http://www.jdon.com/45728
Summary

In this article, we discussed several different ways to add parallelism in a Java application. Starting with our own management of Java threads, we are gradually discovering more advanced solutions, implementing different executor services, Forkjoin frameworks, and actor computing models.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.