Home  >  Article  >  Java  >  In-depth understanding of the synchronized keyword in java

In-depth understanding of the synchronized keyword in java

高洛峰
高洛峰Original
2016-12-13 11:15:321079browse

The synchronized keyword represents the locking of this method, which means that no matter which thread A runs to this method every time, it must check whether there are other threads B (or C D, etc.) that are using this method. If so, it has to wait. Thread B (or C D) that is using this method will run this method after running this method. If not, run it directly, including two usages: synchronized method and synchronized block.

1. synchronized method:
Declare the synchronized method by adding the synchronized keyword to the method declaration. For example:

public synchronized void accessVal(int newVal); 

The synchronized method controls access to class member variables: each class instance corresponds to a lock. Each synchronized method must obtain the lock of the class instance that calls the method before it can be executed. Otherwise, the thread to which it belongs is blocked. Once the method is When executing, the lock is exclusively occupied and the lock is released until the method returns. After that, the blocked thread can obtain the lock and re-enter the executable state. This mechanism ensures that for each class instance at the same time, at most one of all its member functions declared as synchronized is in the executable state (because at most one can obtain the lock corresponding to the class instance), thus effectively avoiding the need for class members to Access violation of variables (as long as all methods that may access class member variables are declared synchronized). In Java, not only class instances, but also each class corresponds to a lock. In this way, we can also declare the static member functions of the class as synchronized to control its access to the static member variables of the class. Defects of the synchronized method: If a large method is declared as synchronized, the efficiency will be greatly affected. Typically, if the thread class method run() is declared as synchronized, it will continue to run during the entire life cycle of the thread. Will cause its calls to any synchronized methods of this class to never succeed. Of course, we can solve this problem by putting the code that accesses class member variables into a special method, declaring it as synchronized, and calling it in the main method, but Java provides us with a better solution, that is synchronized block.

2. synchronized block:
Declare the synchronized block through the synchronized keyword. The syntax is as follows:

synchronized(syncObject)
{  
//允许访问控制的代码  
} 

synchronized block is a code block in which the code must obtain the lock of the object syncObject (as mentioned before, it can be a class instance or class) before it can be executed. The specific mechanism is the same as mentioned above. Because it can target any code block and specify the locked object arbitrarily, it has high flexibility.

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

2. When a thread accesses a synchronized(this) synchronized code block of object, other threads' access to all other synchronized(this) synchronized code blocks in object will be blocked.

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

4. The third example is also applicable to other synchronization code blocks. That is to say, when a thread accesses a synchronized(this) synchronized code block of object, it obtains the object lock of this object. As a result, other threads' access to all synchronized code parts of the object object is temporarily blocked. ​

5. The above rules also apply to other object locks.

A simple example of synchronized

public class TextThread
{
/**
* @param args
*/
public static void main(String[] args)
{
// TODO 自动生成方法存根
        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, in order to create a time difference, which is the opportunity for errors, Thread.sleep(10) is used. Java’s support for multi-threading and synchronization mechanism are deeply loved by everyone. It seems that By using the synchronized keyword, you can easily solve the problem of multi-thread shared data synchronization. How? ——It is necessary to have an in-depth understanding of the role of the synchronized keyword before we can make a conclusion.

In general, the synchronized keyword can be used as a modifier of a function, or as a statement within a function, which is what we usually call synchronized methods and synchronized statement blocks. If classified further, synchronized can act on instance variables, object references, static functions and class literals (class name literals). Before elaborating further, we need to clarify a few points:

A. Regardless of whether the synchronized keyword is added to a method or an object, the lock it acquires is an object, rather than treating a piece of code or a function as a lock - and the synchronized method is likely to be accessed by objects from other threads.

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

C. Achieving synchronization requires a lot of system overhead and may even cause deadlock, so try to avoid unnecessary synchronization control.

Next, let’s discuss the impact of synchronized on the code when used in different places: Assume that P1 and P2 are different objects of the same class. This class defines synchronization blocks or synchronization methods in the following situations. Both P1 and P2 can be used. Call them.

1. When using synchronized as a function modifier, the sample code is as follows:

Public synchronized void methodAAA()
{
//…
}

这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。上边的示例代码等同于如下代码:

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

(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱。


2.同步块,示例代码如下:

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

这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:

class Foo implements Runnable
{
        private byte[] lock = new byte[0]; // 特殊的instance变量
        Public void methodA()
        {
           synchronized(lock) { //… }
        }
        //…..
}

注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

3.将synchronized作用于static 函数,示例代码如下:

Class Foo
{
    public synchronized static void methodAAA()   // 同步的static 函数
    {
        //….
    }
    public void methodBBB()
    {
       synchronized(Foo.class)   // class literal(类名称字面常量)
    }
}

代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。

记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。

小结如下:

搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。 还有一些技巧可以让我们对共享资源的同步访问更加安全:1.定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。2.如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了  还有,比较常用的就有:Collections.synchronizedMap(new HashMap()),当然这个MAP就是生命在类中的全局变量,就是一个线程安全的HashMap,web的application是全web容器公用的,所以要使用线程安全来保证数据的正确。


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn