Talk about concurrent Programming (V): Collaboration between threads

Source: Internet
Author: User

Writing multithreaded programs requires thread collaboration, and the previous introduction of using mutexes 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 can be executed in parallel, but some steps require that all tasks end before they can be activated.
wait () and Notifyall ()Wait () allows you to wait for a condition to change, and wait () suspends the task while waiting for changes 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. It is important to understand that the lock is not released when you call Sleep (), and it is also the case to call yield (). On the other hand, when a task encounters a call to wait () in the method, the execution of the thread is suspended and the lock on the object is freed.     So wait () releases the lock, which means that another task can get the lock, so other synchronized methods in the object can be called during wait (), while other methods will usually have a change, which is the change that causes the suspended task to wake up again. There are two forms of wait (). The first version accepts milliseconds as the parameter, meaning the same as the parameter in the Sleep () method, which means "pause during this period." But unlike sleep (), for Wait ():
    1. Object locks are freed during wait ()
    2. You can resume execution from wait () by Notify (), Notifyall (), or by the time it expires.
      The second and more common form of wait () does not receive any parameters. This wait () waits indefinitely until the thread receives a notify () or Notifyall () message.       As you can imagine, wait (), notify (), Notifyall () must be based on something that is attached to its state to achieve this notification and state change. Consider the design method: 1. This kind of thing can be defined by itself. 2. Provide the implementation of the "thing" in object.   Obviously the second way is much easier and easier to move, and more aggressive. Second, this kind of thing may not be thread-safe, so locks are needed to support it. It is a good thing to use synchronized for synchronous protection, because the implementation of "things" is in object, and secondly the advantage of using synchronized is to avoid the non-correspondence of Wait () and notifyall due to inconsistent locks. Wait () releases the lock in a lock, and the notifyall is not related to the operation of the other lock.      java requires that wait (), notify (), and Notifyall () be called only in a synchronous control method or in a synchronization control block.       Here's an example of painting a wax onto a car and polishing it. The polishing task is not able to perform its work until the wax task is completed, while the wax task must wait for the polishing task to finish before applying another layer of wax. Both Waxon and Waxoff use the car object, which suspends and restarts these tasks by using wait () and notifyall () when these tasks wait for conditions to change:
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 example emphasizes that you must surround wait () with a while loop that examines the conditions of interest. This is important because:
    • You may have multiple tasks waiting for a lock for the same reason, and the first wake-up task may have changed the situation (even if you did not, someone would have inherited your class to do so). If this is the case, then 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 this case must be used (Notifyall)). In this case, you need to check if it has been awakened by the correct cause, and if not, call Wait () again.

notify () and Notifyall ()Because it is technically possible that more than one task will be in the wait () state on a single car object, 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 () instead of Notifyall (). Using Notify () instead of Notifyall () is an optimization. with notify (), only one of the many tasks waiting for the same lock will be awakened, so if you want to use Notify () you must ensure that the appropriate task is awakened. Also, in order to use Notify (), all tasks must wait for the same conditions, because if you have multiple tasks waiting for different conditions, then you will not know whether to wake up the proper task. If you use Notify (), you must have only one task to benefit from when the condition changes. Finally, these restrictions must always work for all subclasses that may exist. If any of these rules are not satisfied, then you must use Notifyall () instead of notify ().
using Wait () and Notifyall () to implement producer consumer issues when using Wait () and Notifyall (), it is important to note that there is no two-tiered nesting of synchronized, and if two layers are used, the sycnhronized plus lock on the outer layer cannot be freed. It is also important to note that lock cannot be used to restrict access to resources because the lock cannot be freed when wait. It is still difficult to achieve this problem if you have to limit the notifyall to the same kind of notifyall. The following paste a humble implementation of their 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 can prove that it is thread-safe. However, using this method is too obscure, the mechanism of producer consumer problem needs us to control, in fact, Java Concurrency Class library provides us with the implementation of this model, we will use blocking queue to rewrite the problem.
using an explicit lock and condition objectWe can use the condition object explicitly instead of the "things" I mentioned earlier, which will be more flexible and clearer, but will increase the number of objects in the program. You can suspend a task by raising an await () on the condition.     When the external condition changes, meaning that a task should continue to execute, you can call signal () to notify the task, either by waking up a task, or by calling Signalall () to wake up all tasks that are suspended on this condition. Let's 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, and we can use synchronous queues to solve the problem of task collaboration, in which the synchronization queue allows only one task to insert or remove elements at any given time. This queue is provided in the Java.util.concurrent.BlockingQueue interface, which has a large number of implementations.     You can usually use Linkedblockingqueue, which is an unbounded queue, and you can also use Arrayblockingqueue, which has a fixed size, so you can place a limited number of elements into it before it is blocked. If 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 more elements are available.     Blocking queues can solve a very large number of problems, which are simpler and more reliable than wait () and Notifyall (). The above restaurant problem is implemented using the blocking queue.
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 counterpart in the Java input/Output class library is the PipedWriter class (which allows tasks to be written to the pipeline) and the Pipedreader class (which allows different tasks to be read from the same pipeline). 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 versions prior to introducing 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 (); }}


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.