Talk about concurrent Programming (V): Collaboration between threads

Source: Internet
Author: User

Thread collaboration is required to write multithreaded programs. The previous introduction of the use of mutual exclusion to prevent thread racing is to solve the derivative harm of thread collaboration. The key to writing a thread collaborator is to solve the problem of coordination between threads, in which some of these tasks can run in parallel, but some steps require that all tasks be completed.
wait () and Notifyall ()Wait () allows you to wait for a condition to change, and wait () suspends the task while waiting for a change in the outside world, and only when notify () or Notifyall () occurs, which means something of interest has occurred. This task will be awakened and checked for the resulting changes.

When you call Sleep (), the lock is not released, and the call yield () is also in this case. Understanding this is critical. There is one more aspect. When a task encounters a call to wait () in the method. The thread's run is suspended, and the lock on the object is freed.

So wait () releases the lock. This means there is one more task to get the lock. So the other synchronized methods in the object can be called during wait (), while other methods will usually have a change, which is what makes the suspended task awaken again to the changes of interest.

There are two forms of wait (). The first version number accepts a number of milliseconds, meaning the same as the meaning of the parameters in the Sleep () method, which means "pause during this period." But unlike sleep (), for Wait ():
    1. Object locks are freed during wait ()
    2. Can pass notify (), Notifyall (), or time expires. Resumes running from wait ().
Another, and more common form of wait () does not receive any number of parameters.

Such a wait () waits indefinitely until the thread receives a notify () or Notifyall () message.

Can imagine that wait (), notify (), Notifyall () must be based on a "thing", the state of their own to attach, to achieve such notification and state changes.

Consider the design method: 1. Such things can be defined individually.  2. Provide the implementation of the "thing" in object. Obviously another way is much easier and more convenient. More mobility. Secondly. Such things may not be thread-safe, so locks are required to support them.

The use of synchronized to synchronize the protection is rightfully, because the "thing" implementation is in the object, followed by the advantage of using synchronized is to some extent to avoid the lock due to inconsistencies in the case of the Wait () and notifyall should be incorrect. Wait () releases the lock in a lock, and Notifyall has no correlation with the operation of a lock.

     java requires only the wait (), notify (), and Notifyall () to be called in the synchronization control method or in the synchronization control block.       The following demo example. One is to paint the wax on the car and one is to polish it. The polishing task cannot run its work until the wax task is finished. The wax-painting task must wait for the polishing task to finish before it is coated with a layer of wax. Both Waxon and Waxoff use the car object, which, when these tasks wait for a condition change, uses wait () and Notifyall () to suspend and start these tasks again:
Class Car {Private Boolean waxon = false;          Public synchronized void waxed () {Waxon = true;     Notifyall ();          } public synchronized void buffed () {Waxon = false;     Notifyall ();               synchronized void Waitforwaxing () throws interruptedexception{while (Waxon = False)     Wait ();               synchronized void Waitforbuffing () throws Interruptedexception {while (Waxon = true)     Wait ();     }}class Waxon implements Runnable {private car car;     Public Waxon (car c) {car = c;} public void Run () {try {while (!                    Thread.interrupted ()) {System.out.print ("Wax on!");                    TimeUnit.MILLISECONDS.sleep (200);                    Car.waxed ();               Car.waitforbuffing ();          }} catch (Interruptedexception e) {System.out.println ("Exiting via interrupt"); } SyStem.out.println ("Ending Wax on Task");     }}class Waxoff implements Runnable {private car car;     Public Waxoff (car c) {car = c;} public void Run () {try {while (!                    Thread.interrupted ()) {car.waitforwaxing ();                    System.out.print ("Wax Off");                    TimeUnit.MILLISECONDS.sleep (200);               Car.buffed ();          }} catch (Interruptedexception e) {System.out.println ("Exiting via interrupt");     } System.out.println ("Ending Wax Off task");          }}public class Waxomatic {public static void main (string[] args) throws exception{car car = new car ();          Executorservice exec = Executors.newcachedthreadpool ();          Exec.execute (new Waxoff (car));          Exec.execute (new Waxon (car));          TimeUnit.SECONDS.sleep (5);     Exec.shutdownnow (); }}
The previous demo example emphasizes that you must surround wait () with a while loop that examines the conditions of interest.

This is very important due to:

    • You may have multiple tasks waiting for a lock for the same reason, and the first wake-up task may have changed such a situation (even if you did not, someone would have inherited your class to do so). Assuming this is the case, the task should be suspended again until the conditions in which it is interested change.

    • It is also possible for certain tasks to be in different causes while waiting for your object to be locked (in such a case must be used (Notifyall)). In such a case, you need to check if it has been awakened by the correct reason, and if not, call wait again ().

notify () and Notifyall ()Because of the technical, there may be multiple tasks in the wait () state on a single car object. Therefore, calling Notifyall () is more secure than calling Notify ().     However, the structure of the above program will only have one task in the Wait () state, so you can use notify () to replace Notifyall (). Using Notify () instead of Notifyall () is an optimization.

When using Notify (). Only one of the many tasks waiting for the same lock will be awakened, so assume that you want to use Notify () to ensure that the appropriate task is awakened.

In addition, in order to use Notify (). All tasks must wait for the same conditions, due to the assumption that you have multiple tasks waiting for different conditions. Then you will not know whether to awaken the proper task. Assuming that notify () is used, there must be only one task that can benefit from a change in conditions. Finally, these restrictions must always work for all subclasses that may exist.

Let's assume that none of these rules is satisfied. Then you have to use Notifyall () instead of notify ().


using Wait () and Notifyall () to implement producer consumer issueswhen using Wait () and Notifyall () it is important to note that there is no two-tiered nesting of synchronized, assuming that two layers are used, the outer sycnhronized plus lock cannot be freed.

It is also important to note that lock cannot be used to restrict access to resources. The lock cannot be freed because of wait. Suppose you have to limit your notifyall to the same notifyall. It is still difficult to achieve this problem.

The following affixed a humble realization of one's own. Friends have beautiful code can also be affixed to the exchange.
Class Meal {}class Waitperson implements Runnable {private String name;     Private Restaurant Restaurant;          Public Waitperson (String name, Restaurant res) {this.name = name;     This.restaurant = res; } @Override public void Run () {try {while (! Thread.interrupted ()) {synchronized (restaurant.waitpersons) {while (Restaura                         Nt.meals.size () < 1) {restaurant.waitPersons.wait (); }} synchronized (Restaurant.chefs) {if (RESTAURANT.MEALS.S                              Ize () >= 1) {restaurant.meals.poll ();                              Restaurant.chefs.notifyAll ();                         SYSTEM.OUT.PRINTLN (name + "Consumed a meal!"); }}}} catch (Interruptedexception e) {SYSTEM.OUT.PRINTLN (name + "is ended via interruptedexception!");          Return     } System.out.println (name + "is ended via interruptedexception!");     }}class Chef implements Runnable {private String name;     Private Restaurant Restaurant;          Public Chef (String name, Restaurant res) {this.name = name;     This.restaurant = res; } @Override public void Run () {try {while (! Thread.interrupted ()) {synchronized (restaurant.chefs) {while (Restaurant.mea                    Ls.size () >) {restaurant.chefs.wait (); }} synchronized (Restaurant.waitpersons) {if (restaurant.meals.                              Size () <= {restaurant.meals.add (New Meal ());                              Restaurant.waitPersons.notifyAll (); SYSTEM.OUT.PRINTLN (name + "produced a Me"Al! ");}}}               catch (Interruptedexception e) {System.out.println (name + "is ended via interruptedexception!");          Return     } System.out.println (name + "is ended via interruptedexception!");     }}public class Restaurant {public queue<meal> meals = new concurrentlinkedqueue<meal> ();     Public list<waitperson> waitpersons = new arraylist<waitperson> ();     Public list<chef> chefs = new arraylist<chef> ();          public static void Main (string[] args) throws Interruptedexception {Restaurant res = new Restaurant ();          Executorservice exec = Executors.newcachedthreadpool ();          Chef chef1 = new Chef ("Chef1", res);          Chef Chef2 = new Chef ("Chef2", res);          Res.chefs.add (CHEF1);          Res.chefs.add (CHEF2);          Exec.execute (CHEF1);          Exec.execute (CHEF2); Waitperson WaitPerson1 = new WaiTperson ("WaitPerson1", res);          Waitperson waitPerson2 = new Waitperson ("WaitPerson2", res);          Res.waitPersons.add (WaitPerson1);          Res.waitPersons.add (WaitPerson2);          Exec.execute (WaitPerson1);          Exec.execute (WaitPerson2);          TimeUnit.MILLISECONDS.sleep (3000);     Exec.shutdownnow (); }}
The above program proves to be thread-safe.

Just using such a way is really too obscure. The mechanism of producer consumer problems requires us to control, in fact, the Java Concurrency Class library provides us with the implementation of such a model, we will later use the blocking queue to rewrite the problem.


using an explicit lock and condition objectWe were able to use the condition object explicitly instead of the "stuff" I mentioned earlier, and in this way it would be more flexible and clearer, but would add the number of objects in the program. You can suspend a task by raising the condition with await (). When external conditions change. means that a task should continue to run.     You can call signal () to notify the task, either by waking up a task, or by calling Signalall () to wake up all the tasks on the condition that are suspended by itself. Here we use this tool to rewrite the car class in the previous example.

Class Car {Private Boolean waxon = false;     Private lock lock = new Reentrantlock ();     Private Condition Condition = Lock.newcondition ();          public void Waxed () {lock.lock ();               try {Waxon = true;          Condition.signalall ();          } finally {Lock.unlock ();          }} public void buffed () {lock.lock ();               try {Waxon = false;          Condition.signalall ();          } finally {Lock.unlock ();          }} public void Waitforwaxing () throws interruptedexception{Lock.lock ();          try{while (Waxon = = False) condition.await ();          } finally {Lock.unlock ();          }} public void Waitforbuffing () throws Interruptedexception {Lock.lock ();          try {while (Waxon = = True) condition.await ();    } finally {Lock.unlock ();      }     }} 

using Blockingqueue to solve producer consumer problemsJava helps us abstract the issue of producer consumers. We were able to use synchronization queues to solve the problem of task collaboration. Synchronization queues at any given moment simply agree to one task inserting or removing elements.

This queue is provided in the Java.util.concurrent.BlockingQueue interface, which has a large number of implementations. You can usually use Linkedblockingqueue. It is an unbounded queue and can also use Arrayblockingqueue, which has a fixed size, so you can place a limited number of elements into it before it is blocked.

Assuming that a consumer task attempts to get an object from a queue that is now empty, these queues can also suspend consumer tasks and restore consumer tasks when there are many other elements available. Blocking the queue can solve a lot of problems.     It is simpler and more reliable than wait () and Notifyall (). The following is the use of blocking queues to implement the above restaurant problem.

Class Meal {}class Waitperson implements Runnable {private String name;     Private Restaurantblookingqueue Restaurant;          Public Waitperson (String name, restaurantblookingqueue res) {this.name = name;     This.restaurant = res; } @Override public void Run () {try {while (!                    Thread.interrupted ()) {restaurant.meals.take ();                    SYSTEM.OUT.PRINTLN (name + "taked a Meal");               Thread.Sleep (100); }} catch (Interruptedexception e) {System.out.println (name + "is ended via interruptedexception               !");          Return     } System.out.println (name + "is ended via interruptedexception!");     }}class Chef implements Runnable {private String name;     Private Restaurantblookingqueue Restaurant;          Public Chef (String name, restaurantblookingqueue res) {this.name = name;     This.restaurant = res; } @Override    public void Run () {try {while (!                    Thread.interrupted ()) {Restaurant.meals.put (New Meal ());                    System.out.println (THIS.name + "made a meal");               Thread.Sleep (100);  }} catch (Interruptedexception e) {System.out.println (name + "is ended via interruptedexception               !");          Return     } System.out.println (name + "is ended via interruptedexception!"); }}public class Restaurantblookingqueue {public blockingqueue<meal> meals = new Arrayblockingqueue<meal> (1     0);     Public list<waitperson> waitpersons = new arraylist<waitperson> ();     Public list<chef> chefs = new arraylist<chef> (); public static void Main (string[] args) throws Interruptedexception {Restaurantblookingqueue res = new Restaurant          Blookingqueue ();          Executorservice exec = Executors.newcachedthreadpool (); Chef CHEF1 = New Chef ("Chef1", res);          Chef Chef2 = new Chef ("Chef2", res);          Res.chefs.add (CHEF1);          Res.chefs.add (CHEF2);          Exec.execute (CHEF1);          Exec.execute (CHEF2);          Waitperson waitPerson1 = new Waitperson ("WaitPerson1", res);          Waitperson waitPerson2 = new Waitperson ("WaitPerson2", res);          Res.waitPersons.add (WaitPerson1);          Res.waitPersons.add (WaitPerson2);          Exec.execute (WaitPerson1);     Exec.execute (WaitPerson2);     TimeUnit.MILLISECONDS.sleep (3000);     Exec.shutdownnow (); }}

use pipelines for input/output between tasksCommunication between threads via input/output is often useful.

A class library that provides threading functionality supports the input/output of threads in the form of a "pipe".

Their corresponding object in the Java input/Output class library is the PipedWriter class (the consent task is written to the pipeline) and the Pipedreader class (which agrees to read from the same pipeline for different tasks). This model can be seen as a variant of the "producer-consumer" problem.

A pipeline is basically a blocking queue that exists in several Java version numbers before the introduction of Blookingqueue.

Class Sender implements Runnable {private random Rand = new Random (47);     Private PipedWriter out = new PipedWriter ();     Public PipedWriter Getpipedwriter () {return out;}                         public void Run () {try {if (true) {for (char c = ' A '; c <= ' z '; C + +) {                         Out.write (c);                    TimeUnit.MILLISECONDS.sleep (Rand.nextint (500));          }}} catch (IOException e) {System.out.println (e + "Sender write exception");          } catch (Interruptedexception e) {System.out.println (E + "Sender sleep exception");     }}}class Receiver implements Runnable {private pipedreader in;     Public Receiver (sender) throws IOException {in = new Pipedreader (Sender.getpipedwriter ()); public void Run () {try {while (true) {System.out.print ("Read:" + (char) I N.read () + ",");          }} catch (IOException e) {System.out.println (e + "Receiver read exception"); }}}public class Pipedio {public static void main (String []args) throws Exception {Sender sender = new          Sender ();          Receiver receiver = new Receiver (sender);          Executorservice exec = Executors.newcachedthreadpool ();          Exec.execute (sender);          Exec.execute (receiver);          TimeUnit.SECONDS.sleep (4);     Exec.shutdownnow (); }}

dead LockDeadlock is the middle concept of the operating system, because there are very many situations where deadlocks can occur in the operating system. But we often also need to prevent deadlocks in concurrent programs. This is especially the time when multiple threads are concurrently interviewing multiple objects. First, we need to logically avoid the possibility of deadlocks, such as the problem of philosophers eating. The general solution in the program is to allocate resources to it at once, in order to provide concurrency, we need to further narrow the scope of the concurrent lock.

In addition to logically preventing concurrency. We also need to deal with unexpected situations, such as the thread that gets to the resource is dropped halfway. We need to release resources. The lock is released in the program. Can be implemented through Try-catch in the program.


Talk about concurrent Programming (V): Collaboration between threads

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.