In depth understanding of the synchronized keyword in Java

  • 2020-04-01 02:42:57
  • OfStack

Lock, the synchronized keyword, on behalf of this method is equivalent to no matter which one thread every time A run to this method, to check if any other threads are using this method B (C or D, etc.), so wait is using the method of thread B (or C D) after running this method to run this thread A, if not, run directly it includes two kinds of usage: synchronized methods and synchronized blocks.

1. Synchronized methods:
Declare a synchronized method by adding the synchronized keyword to the method declaration. Such as:


public synchronized void accessVal(int newVal); 

Synchronized method to control access to a class member variables: each class instance corresponds to a lock, each a synchronized method must be called the method of class instance lock is able to perform, otherwise affiliated thread block, once method execution, is dominant in the lock, lock release until returned from this method, has been blocked thread in order to get the lock, back into the executable. This mechanism ensures that the same time for each class instance, all its declared as a member of the synchronized function as much as there is only one in the executable state (because only one can get at most the class instance corresponding lock), thus effectively avoid the class member variable access conflict (as long as all may visit a class member variable method is declared synchronized). In Java, not only class instances, each class also has a lock, so we can also declare the class's static member function as synchronized to control its access to the class's static member variables. Disadvantages of synchronized methods: declaring a large method as synchronized can greatly affect efficiency. Typically, declaring the method run() of a thread class as synchronized will cause it to never succeed in calling any of the synchronized methods of this class because it is running for the entire life of the thread. Of course we can solve this problem by putting code that accesses class member variables into specialized methods, declaring them synchronized, and calling them from the main method, but Java provides a better solution: synchronized blocks.

2. The synchronized block:
A synchronized block is declared by the synchronized keyword. The syntax is as follows:


synchronized(syncObject)
{  
//Code that allows access control
} 

A synchronized block is a block of code in which the code must obtain a lock on the object syncObject (which, as mentioned earlier, can be an instance or class) in order to execute, as described earlier. Because you can target any code block, and you can specify any locked object, you have more flexibility.

Some understanding of synchronized (this)
When two concurrent threads access this synchronized(this) synchronized block of code in the same object object, only one thread can execute at a time. Another thread must wait for the current thread to finish executing the block before it can execute.

When a thread accesses a synchronized(this) synchronized block of an object, access by other threads to all other synchronized(this) synchronized blocks of the object is blocked.

However, when a thread accesses a synchronized(this) synchronized block of an object, another thread can still access parts of the object other than the synchronized(this) synchronized block.

The third example also applies to other synchronized code blocks. That is, when a thread accesses a synchronized(this) synchronized block of code of an object, it acquires the object lock of that object. As a result, access by other threads to all synchronized code parts of the object object is temporarily blocked.

The above rules are also applicable to other object locks.

A simple example of synchronized


public class TextThread
{

public static void main(String[] args)
{
//TODO automatically generates method stubs
        TxtThread tt = new TxtThread();
        new Thread(tt).start();
        new Thread(tt).start();
        new Thread(tt).start();
        new Thread(tt).start();
}
}
class TxtThread implements Runnable
{
int num = 100;
String str = new String();
public void run()
{
while (true)
{
   synchronized(str)
   {
   if (num>0)
   {
    try
    {
     Thread.sleep(10);
    }
    catch(Exception e)
    {
     e.getMessage();
    }
    System.out.println(Thread.currentThread().getName()+ "this is "+ num--);
   }
   }
}
}
}

In the above example, to create a time gap, which is the opportunity for error, thread.sleep (10)Java's support for multi-threading and synchronization mechanism is so popular that it seems that the synchronized keyword can be used to easily solve the problem of multi-threaded Shared data synchronization. What the hell? An in-depth understanding of the role of the synchronized keyword is required.

In general, the synchronized keyword can be used as either a modifier of a function or as a statement within a function, which is commonly referred to as a synchronized method and a synchronized statement block. If classified in more detail, synchronized applies to instance variables, object references, static functions, and class literals. Before going any further, we need to make a few points clear:

A. Whether the synchronized keyword is added to a method or an object, the lock it acquires is an object, not a piece of code or a function that ACTS as a lock, and the synchronized method is likely to be accessed by objects in other threads.

B. Each object has only one lock associated with it.

C. Synchronization is costly and can even cause deadlocks, so avoid unnecessary synchronization control.

Let's move on to the impact of synchronized on code in different places: suppose that P1 and P2 are different objects of the same class, and that the class defines synchronized blocks or methods in the following cases, so that P1 and P2 can both be called.

1. When using synchronized as a function modifier, the sample code looks like this:


Public synchronized void methodAAA()
{
// ... 
}

This is the synchronized method, so which object is synchronized locking? What it locks is the synchronous method object that is called. In other words, when an object P1 executes this synchronization method in different threads, they will form mutual exclusion and achieve the effect of synchronization. But another object, P2, produced by the Class to which this object belongs, can call this method with the synchronized keyword at will. The sample code above is equivalent to the following:


public void methodAAA()
{
synchronized (this)      // (1)
{
       // ... ..
}
}

What does this at (1) mean? It refers to the object that calls the method, such as P1. So the essence of a synchronized method is to apply synchronized to an object reference. Only the thread with the lock of the P1 object can call the synchronization method of P1. In the case of P2, the lock of P1 has nothing to do with it. In this case, the program may get rid of the control of the synchronization mechanism and cause data chaos.

2. Synchronization block, the sample code is as follows:


public void method3(SomeObject so)
{
    synchronized(so)
    {
       // ... ..
    }
}

In this case, the lock is the object so, and whoever gets the lock can run the code it controls. This can be done when there is an explicit object as a lock, but when there is no explicit object as a lock and you just want to synchronize a piece of code, you can create a special instance variable (which has to be an object) to act as a lock:


class Foo implements Runnable
{
        private byte[] lock = new byte[0]; //Special instance variable
        Public void methodA()
        {
           synchronized(lock) { // ...  }
        }
        // ... ..
}

Note: the zero-length byte array Object is more efficient to create than any other Object to see the compiled bytecode: only three opcodes are needed to generate a zero-length byte[] Object, whereas Object lock = new Object() requires seven lines of opcodes.

3. Apply synchronized to the static function as follows:


Class Foo
{
    public synchronized static void methodAAA()   //Synchronized static function
    {
        // ... .
    }
    public void methodBBB()
    {
       synchronized(Foo.class)   //Class literal
    }
}

The methodBBB() method in the code is the case where the class literal is the lock, which is the same as the synchronized static function, and the lock is the particular class of the object that is currently calling the method (class, not some concrete object generated by the class).

Remember from Effective Java that using foo.class and p1.getclass () as synchronous locks is not the same as using p1.getclass () to lock the class. P1 refers to the object generated by class Foo. It can be inferred that if A class defines A static function A of synchronized, and also defines A synchronized instance function B, then the same object Obj of that class will not constitute synchronization if it accesses methods A and B separately in multiple threads, because they have different locks. The lock for method A is the Obj object, and the lock for method B is the Class to which Obj belongs.

The summary is as follows:

Knowing which objects are synchronized locked can help us design safer multithreaded programs. There are some other tricks that can make synchronous access to Shared resources more secure: 1. Define the private instance variable + its get method instead of the public/protected instance variable. If a variable is defined as public, the object can take it directly from the outside, bypassing the control of the synchronization method, and change it. This is also one of the standard implementations of javabeans. 2. If the instance variable is an object, such as a group of Numbers or an ArrayList, then the above method is still not safe, because when the external object gets a reference to the instance object through the get method and points it to another object, then the private variable is changed, which is dangerous. At this point you need to add synchronized to the get method as well, and just return the clone() of the private object so that the caller gets a reference to the copy of the object. And are commonly used are: the Collections. The synchronizedMap (new HashMap ()), of course, this MAP is global variables in the class of life, is a thread safe HashMap, web application is the web container public, so you want to use thread safety to ensure that the correct data.


Related articles: