java thread synchronization details and example code

  • 2020-06-12 09:02:17
  • OfStack

java thread synchronization

Summary:

In order to speed up the code, we adopted a multi-threaded approach. Parallel execution does make the code more efficient, but the problem is that there are many threads running in the program at the same time, and if they try to modify one object at the same time, it may be a mistake to use a synchronous mechanism to manage these threads.

(1) Competition conditions

Remember the operating system, let me remember a very impressive picture. Above is a block process, within which there are several threads, all of which line up 1's resources to the process. This is also true in Java, where resources are Shared between threads rather than having a separate resource per thread. In this Shared situation, it is possible to have multiple threads accessing a resource at the same time, a phenomenon we call a race condition.

In a banking system, each thread manages a separate account and these threads may make transfers.
When a thread performs an operation, it first stores the account balance in a register. In step 2, it reduces the number in the register by the amount of money to be transferred. In step 3, it writes the result back to the balance.
The problem is that after this thread completes step 1 or 2, another thread wakes up and modifies the account balance of the first thread without the first thread knowing. After the first thread waits for the second thread to finish executing, it continues its step 3: write the result back to the balance. At this point, it brushes off the operation of the second thread, so the total amount of money for the entire system is bound to error.
This is what happens to the java competition conditions.

(2) ReentrantLock class

The above example tells us that if our operation is not atomic, the interruption will definitely happen, even if the probability is very small sometimes, but it cannot be ruled out. We can't turn our code into an atomic operation like the operating system, what we can do is lock our code for security. In a concurrent program, if we want to access the data, we first give our code a lock. During the time we use the lock, the resources involved in our code are as if they are "locked" and cannot be accessed by other threads until we open the lock.

In java, both the synchronized keyword and the ReentrantLock class have this locking capability. Let's start here with a discussion of ReentrantLcok's functions under 1.

1. ReentrantLock constructor

In this class, two constructors are provided, one is the default constructor, nothing to talk about, and one is the constructor with a fair policy. This fair strategy is first of all slower than a normal lock, and second it is not really fair in some cases. And if we really need a fair strategy for no particular reason, try not to study it.

2. Capture and release


ReentrantLock myLock = new ReentrantLock();
// Create an object 
myLock.lock();
// Acquiring a lock 
try{
...
}
finally{
myLock.unlock();
// Release the lock 
}

1 Do remember to release the lock in finally!! As we said before, unchecked errors cause the thread to terminate. Inexplicable termination will cause the program to stop running down and the lock will not be released until the release is placed in finally. close() is the same reason we use packages in our framework. Speaking of close, it is worth mentioning that we cannot use "try statement with resources" when using a lock because the lock is not closed with close. If you don't know what an try statement with resources is, forget it.

3. The lock has reentrancy

If you want to use locks in recursion or looping, feel free to use them. The ReentrantLock lock is reentrant and maintains a count for each call to lock (), which must be released with unlock for each call.

(3) Condition object

Typically, threads find a problem after locking into a critical section. The resources they need are used in other objects or do not meet the conditions they can execute. In this case, we need a condition object to manage threads that have a lock but cannot do useful work.


if(a>b){
  a.set(b-1);
}

1. "Trapped yourself."

This is a simple conditional, but we can't write it this way in a concurrent program. The problem is that if another thread wakes up just after the thread has made the judgment, and another thread makes a less than b after the operation (the condition in the if statement is no longer correct).

At this point it might occur to us to put the entire if statement directly in the lock to ensure that our code is not broken. However, there is another problem. If if determines false, the statement in if will not be executed. But if we need to implement the statement in if, even we wait for 1 straight after if variable of the right to enforce the if statements, at this moment, we suddenly found that if statement never became correct, because we put the thread lock lock, other threads can't access the value of critical section and modify a and b make if judgment is right, it is really very embarrassed, our own lock ourselves stuck, we couldn't go out, not to others.

2. Condition class

To solve this, we use the newCondition method in the ReentrantLock class to get a condition object.


Condition cd = myLock.newCondition();

Once we have acquired the Condition object, it is time to investigate what methods and functions this object has. Without looking at API, we come back to the question of if conditional judgment, how can we: if we find that if is wrong when it is locked, give the other thread a chance and wait for if to be right.

The Condition class is designed to solve this problem. With the Condition class, we follow the await method directly under the if statement, which means that the thread is blocked, gives up the lock, and so on.

Note that the term we use here is blocking, and we said earlier that blocking is very different from waiting: once a lock is free, it will automatically acquire the lock, while blocking obtains the lock, even if there is a free lock, it will wait until the thread scheduler allows it to hold the lock.

Other threads that have successfully executed the if statement will call the signalAll method, which will reactivate all threads blocked because of this condition, allowing the threads to resume their work from the blocked point. At this point, the thread should test the condition again, and if it still doesn't meet the condition, it needs to do the same again.


ReentrantLock myLock = new ReentrantLock();
// Create a lock object 
myLock.lock();
// Lock the bottom critical section 

Condition cd = myLock.newCondition();
// create 1 a Condition Object, this cd Object represents a condition object 

while(!(a>b))
  cd.await();
// The above while Circulation and await Method calls are standard writing 
// If it doesn't satisfy if It will block, abandon the lock, and wait for someone else to activate it 

a.set(b-1);
//1 Until the while We loop it out, satisfy the judgment, we perform our function 

cd.signalAll();
// The last 1 Don't forget to call signalAll Method to activate another blocked thread 
// If all threads are waiting for other threads signalAll , then enter deadlock 


Very ominously, if all threads are waiting for another thread, signalAll, you enter a deadlock state. A deadlock state is a situation in which all the resources needed by one thread are looped by another thread so that no one can execute. Finally, it is necessary to call the signalAll method to activate the other "brothers" that are blocked because of cd, so that you and I can avoid deadlock.

3.Condition object and lock summary

In summary, Condition objects and locks have several characteristics.

Locks can be used to protect snippets of code, and only one thread can enter the protected area at any one time A lock can manage threads trying to enter a critical section A lock can have one or more condition objects Each condition object manages threads that cannot be executed for the reasons described earlier but have entered the protected code segment

(4) synchronized keyword

The ReentrantLock and Condition objects we described above are one way to protect code snippets, and there is another mechanism in java that adds an internal lock to a method by modifying it with the keyword synchronized. Starting with version 1, each object in java has an internal lock, each of which protects methods decorated with synchronized. That is, if you want to call this method, you first need to get the internal object lock.

1. Comparison between synchronized and ReentrantLock

Let's take the above code first:


public void function(){
  ReentrantLock myLock = new ReentrantLock();
  myLock.lock();

  Condition cd = myLock.newCondition();

  while(!(a>b))
    cd.await();

  a.set(b-1);

  cd.signalAll();
}

If we implemented this code with synchronized, it would look something like this:


public synchronized void function(){
  while(!(a>b))
    wait();

  a.set(b-1);

  notifyAll();
}

It is important to note that there is no need to use the ReentrantLock and Condition objects when using the synchronized keyword. We replace the await method with the wait method and the notifyAll method with the signalAll method. It's actually a lot easier to write this way than before.

synchronized for static methods

It is also legal to declare static methods as synchronized. If this method is called, the internal lock of the associated class object is obtained. For example, if we call a static method in the Test class, the lock on the Test.class object will be locked.

3. Limitations of internal locks and conditions

Although the internal lock is simple, it has many limitations:

You cannot interrupt a thread that is trying to acquire a lock Timeout cannot be set while attempting to obtain a lock Because conditions cannot be instantiated with Condition. The condition that each lock has a single 1 May not be sufficient

Which of these two locks should be used in your code? Lock and Condition objects or synchronized methods? There are some Suggestions in core java1:

It is best to use neither ReentrantLock nor synchronized keywords. In many cases you can use the ES175en.util.concurrent package If synchronized meets your code needs, use it first Don't use ReentrantLcok until you really need it

Thank you for reading, I hope to help you, thank you for your support to this site!


Related articles: