Home>

java thread synchronization

When two or more threads need to share resources,They need some way to determine that resources are occupied by only one thread at a time.The process to achieve this is called synchronization. Like you see,java provides unique for this,Language level support.

The key to synchronization is the concept of management (also called semaphore). Guan Cheng is a mutex exclusive lock object,Or mutex. At a given time,Only one thread can get the supervisor.When a thread needs to lock,It must go into management.All other threads trying to enter the locked process must suspend until the first thread exits the process.These other threads are called waiting processes.A thread with a supervisor can enter the same supervisor again if it wishes.

If you have used synchronization in other languages ​​such as c or c++,You know it's a little weird to use.This is because many languages ​​do not support synchronization themselves.Instead, for synchronous threads,The program must use the operating system source language.Fortunately, Java implements synchronization through language elements.Most of the complexity associated with synchronization is eliminated.

You can synchronize code in two ways.Both include the use of the synchronized keyword,These two methods are explained separately below.

Using the synchronization method

Synchronization in java is simple,Because all objects have their corresponding implicit procedures.Into the management of an object,Is to call the method modified by the synchronized keyword.When a thread is inside a synchronous method,All other threads of the same instance trying to call this method (or other synchronization method) must wait.In order to exit the management process,And give up control of the object to other waiting threads,The thread that owns the supervisor only needs to return from the synchronization method.

To understand the need for synchronization,Let's start with a simple example that should use synchronization but is useless.The following program has three simple classes.The first is callme, which has a simple method call (). The call () method has a string parameter named msg. This method attempts to print the msg string within square brackets. The interesting thing is that after calling call () to print the left parenthesis and msg string,Call thread.sleep (1000). This method pauses the current thread for 1 second.

The constructor caller of the next class refers to an instance of callme and a string, which are stored in target and msg respectively. The constructor also creates a new thread that calls the run () method of the object.The thread starts immediately.The run () method of the caller class calls the call () method of the callme instance target with the msg string. Finally, the synch class starts by creating a simple instance of callme and three instances of caller with different message strings.

The same instance of callme is passed to each caller instance.

//this program is not synchronized.
class callme {
  void call (string msg) {
    system.out.print ("[" + msg);
    try {
      thread.sleep (1000);
    } catch (interruptedexception e) {
      system.out.println ("interrupted");
    }
    system.out.println ("]");
  }
}
class caller implements runnable {
  string msg;
  callme target;
  thread t;
  public caller (callme targ, string s) {
    target=targ;
    msg=s;
    t=new thread (this);
    t.start ();
  }
  public void run () {
    target.call (msg);
  }
}
class synch {
  public static void main (string args []) {
    callme target=new callme ();
    caller ob1=new caller (target, "hello");
    caller ob2=new caller (target, "synchronized");
    caller ob3=new caller (target, "world");
    //wait for threads to end
    try {
     ob1.t.join ();
     ob2.t.join ();
     ob3.t.join ();
    } catch (interruptedexception e) {
     system.out.println ("interrupted");
    }
  }
}

The output of this program is as follows:

hello [synchronized [world]
]
]

In this example,By calling sleep (), the call () method allows execution to transition to another thread.The result is a mixed output of three message strings.In this program,No method exists that prevents three threads from calling the same method of the same object at the same time.This is a competition,Because three threads compete for the completion method.The example problem uses sleep () to make the effect repetitive and obvious.In most cases,Competition is more complex and unpredictable,Because you can't be sure when a context switch will happen.This causes the program to run from time to time with errors.

In order to achieve the purpose of the above example,Must have the right to use call () continuously. That is,At some point,You must restrict only one thread to dominate it.To do this, you simply add the keyword synchronized before the call () definition, as follows:

class callme {
  synchronized void call (string msg) {
    ...

This prevents other threads from entering call () when one thread uses call (). After synchronized is added before call (),The program output is as follows:

[hello]
[synchronized]
[world]

Anytime in a multi-threaded situation,You have a method or methods that manipulate the internal state of the object,You must use the synchronized keyword to prevent state contention.Remember, once the thread enters the synchronization method of the instance,No other thread can enter the synchronization method of the same instance.However, other asynchronous methods of this instance can still be called.

Synchronization statement

Although creating a synchronization method inside the created class is a simple and effective way to get synchronization,But it does not work at all times.The reason for this,Please follow along.Suppose i want to get synchronous access to class objects that are not designed for multithreaded accessThat is, the class does not use the synchronized method. Moreover, the class is not your own,Created by a third party,You can't get its source code.In this way, you cannot add the synchronized modifier before the related method. How can you synchronize an object of this class?Fortunately, the solution is simple:you only need to place calls to the methods defined by this class in a synchronized block.

Here is the general form of a synchronized statement:

synchronized (object) {
  //statements to be synchronized
}

Among them, object is a reference to the synchronized object.If all i want to synchronize is one statement,Then no curly braces are needed.A synchronization block ensures that calls to object member methods occur only after the current thread successfully enters the object manager.

Here is a modified version of the previous program,A sync block is used in the run () method:

//this program uses a synchronized block.
class callme {
  void call (string msg) {
    system.out.print ("[" + msg);
    try {
      thread.sleep (1000);
    } catch (interruptedexception e) {
      system.out.println ("interrupted");
    }
    system.out.println ("]");
  }
}
class caller implements runnable {
  string msg;
  callme target;
  thread t;
  public caller (callme targ, string s) {
    target=targ;
    msg=s;
    t=new thread (this);
    t.start ();
  }
  //synchronize calls to call ()
  public void run () {
    synchronized (target) {//synchronized block
      target.call (msg);
    }
  }
}
class synch1 {
  public static void main (string args []) {
    callme target=new callme ();
    caller ob1=new caller (target, "hello");
    caller ob2=new caller (target, "synchronized");
    caller ob3=new caller (target, "world");
    //wait for threads to end
    try {
      ob1.t.join ();
      ob2.t.join ();
      ob3.t.join ();
    } catch (interruptedexception e) {
      system.out.println ("interrupted");
    }
  }
}

Here, the call () method is not modified by synchronized. The synchronized is declared in the run () method of the caller class.This gives the same correct result as in the previous example,Because each thread waits for the previous thread to finish before running.

java inter-thread communication

Multithreading replaces event loop programs by dividing tasks into discrete and logical units.Thread has a second advantage:it is far from polling.Polling is usually achieved by a cycle of repeated monitoring conditions.Once the conditions are met,Take appropriate action.This is wasting CPU time. for example,Consider the classic sequence problem,When a thread is producing data and another program is consuming it.To make the question more interesting,Assume that the data generator must wait for the consumer to finish the work before generating new data.In the polling system,Consumers waste a lot of CPU cycles while waiting for data from the producer. Once the producer has finished the work,It will start polling,Waste more cpu time waiting for consumers to finish their work,So go on.Obviously, this situation is not welcome.

To avoid polling,Java includes an inter-process communication mechanism implemented by the wait (), notify (), and notifyall () methods.These methods are implemented in the object using the final method,So all classes contain them.These three methods can only be called in synchronized methods.Although these methods are highly advanced in concept from the perspective of computer science,It is very simple to use in practice:

wait () tells the called thread to give up the supervisor and go to sleep until other threads enter the same supervisor and call notify ().

notify () resumes the first thread in the same object that calls wait ().

notifyall () resumes all threads calling wait () in the same object. The thread with the highest priority runs first.

These methods are declared in the object,As follows:

 final void wait () throws interruptedexception
  final void notify ()
  final void notifyall ()

wait () exists in another form that allows you to define the wait time.

The following example program incorrectly implements a simple producer/consumer problem.It consists of four classes:q, trying to get synchronized sequences;producer, producing queued thread objects;consumer, a thread object that consumes a sequence;And pc, create a single subclass of q, producer, and consumer.

//an incorrect implementation of a producer and consumer.
class q {
  int n;
  synchronized int get () {
    system.out.println ("got:" + n);
    return n;
  }
  synchronized void put (int n) {
    this.n=n;
    system.out.println ("put:" + n);
  }
}
class producer implements runnable {
  q q;
  producer (q q) {
    this.q=q;
    new thread (this, "producer"). start ();
  }
  public void run () {
    int i=0;
    while (true) {
      q.put (i ++);
    }
  }
}
class consumer implements runnable {
  q q;
  consumer (q q) {
    this.q=q;
    new thread (this, "consumer"). start ();
  }
  public void run () {
    while (true) {
      q.get ();
    }
  }
}
class pc {
  public static void main (string args []) {
    q q=new q ();
    new producer (q);
    new consumer (q);
    system.out.println ("press control-c to stop.");
  }
}

Although the put () and get () methods in the q class are synchronized,Nothing prevents producers from surpassing consumers,Nothing prevents consumers from consuming the same sequence twice.As a result, you get the following error output (the output will vary depending on processor speed and tasks loaded):

put:1
got:1
got:1
got:1
got:1
got:1
put:2
put:3
put:4
put:5
put:6
put:7
got:7

After the producer generates 1, the consumer gets the same one or five times in turn. Producers continue to generate 2 to 7, consumers have no chance to get them.

The correct way to write this program in java is to use wait () and notify () to mark both directions.As follows:

//a correct implementation of a producer and consumer.
class q {
  int n;
  boolean valueset=false;
  synchronized int get () {
    if (! valueset)
      try {
        wait ();
      } catch (interruptedexception e) {
        system.out.println ("interruptedexception caught");
      }
      system.out.println ("got:" + n);
      valueset=false;
      notify ();
      return n;
    }
    synchronized void put (int n) {
      if (valueset)
      try {
        wait ();
      } catch (interruptedexception e) {
        system.out.println ("interruptedexception caught");
      }
      this.n=n;
      valueset=true;
      system.out.println ("put:" + n);
      notify ();
    }
  }
  class producer implements runnable {
    q q;
    producer (q q) {
    this.q=q;
    new thread (this, "producer"). start ();
  }
  public void run () {
    int i=0;
    while (true) {
      q.put (i ++);
    }
  }
}
class consumer implements runnable {
  q q;
  consumer (q q) {
    this.q=q;
    new thread (this, "consumer"). start ();
  }
  public void run () {
    while (true) {
      q.get ();
    }
  }
}
class pcfixed {
  public static void main (string args []) {
    q q=new q ();
    new producer (q);
    new consumer (q);
    system.out.println ("press control-c to stop.");
  }
}

Internal get (), wait () are called. This suspends execution until the producer tells the data that it is ready.At this time, the internal get () is resumed.After getting the data,get () calls notify (). This tells the producer that it can enter more data into the sequence.Within put (), wait () suspends execution until the consumer removes the items in the sequence.When execution continues,The next data item is put into the sequence,notify () is called, which informs the consumer that it should remove the data.

Here is the output of the program,It clearly shows the synchronization behavior:

put:1
got:1
put:2
got:2
put:3
got:3
put:4
got:4
put:5
got:5
  • Previous C # NET method for automatic webpage login
  • Next pacejs page loading progress bar plugin