Home > Java > javaTutorial > body text

Usage of java synchronized keyword

高洛峰
Release: 2017-01-05 17:12:51
Original
1369 people have browsed it

0. Leading question code

The following code demonstrates a counter. Two threads perform cumulative operations on i at the same time, each executing 1,000,000 times. The result we expect is definitely i=2000000. But After we execute it multiple times, we will find that the value of i is always less than 2000000. This is because when two threads write to i at the same time, the result of one thread will overwrite the other.

public class AccountingSync implements Runnable {
  static int i = 0;
  public void increase() {
    i++;
  }
  
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      increase();
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    AccountingSync accountingSync = new AccountingSync();
  
    Thread t1 = new Thread(accountingSync);
    Thread t2 = new Thread(accountingSync);
  
    t1.start();
    t2.start();
  
    t1.join();
    t2.join();
  
    System.out.println(i);
  }
}
Copy after login

To fundamentally solve this problem, we must ensure that multiple threads are completely synchronized when operating on i. That is to say, when thread A writes to i , B thread cannot not only write, but also cannot read.

1. The role of the synchronized keyword

The function of the synchronized keyword is actually to achieve synchronization between threads . Its job is to lock the synchronized code so that only one thread can enter the synchronized block at a time, thus ensuring the security between threads. Just like in the above code, i++ can only operate on one thread at the same time. During execution.

2. Usage of synchronized keyword

Specify object lock: lock the given object, enter the synchronized code block to obtain the lock of the given object

Acts directly on instance methods: equivalent to locking the current instance. Entering the synchronization code block requires obtaining the lock of the current instance (this requires the same Runnable instance to be used when creating Thread)

Acts directly on static methods: equivalent to locking the current class. Before entering the synchronization code block, you must obtain the lock of the current class.

2.1 Lock the specified object

Below The code applies synchronized to a given object. One thing to note here is that the given object must be static, otherwise every time we create a new thread, the object will not be shared with each other, and the meaning of locking will be meaningless. Exists.

public class AccountingSync implements Runnable {
  final static Object OBJECT = new Object();
  
  static int i = 0;
  public void increase() {
    i++;
  }
  
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      synchronized (OBJECT) {
        increase();
      }
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(new AccountingSync());
    Thread t2 = new Thread(new AccountingSync());
  
    t1.start();
    t2.start();
  
    t1.join();
    t2.join();
  
    System.out.println(i);
  }
}
Copy after login

2.2 Acts directly on instance methods


synchronized The keyword acts on instance methods, which means that before entering the increase() method, the thread must obtain the lock of the current instance. This requires us to use the same Runnable object instance when creating a Thread instance. Otherwise, the thread's The locks are not on the same instance, so there is no way to talk about locking/synchronization issues.

public class AccountingSync implements Runnable {
  static int i = 0;
  public synchronized void increase() {
    i++;
  }
  
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      increase();
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    AccountingSync accountingSync = new AccountingSync();
  
    Thread t1 = new Thread(accountingSync);
    Thread t2 = new Thread(accountingSync);
  
    t1.start();
    t2.start();
  
    t1.join();
    t2.join();
  
    System.out.println(i);
  }
}
Copy after login

Please pay attention to the first three lines of the main method, describing the keywords The correct usage of acting on instance methods.

2.3 Acting directly on static methods

Use the synchronized keyword on static methods, so there is no need to use two The threads must point to the same Runnable method. Because the method block needs to request the lock of the current class, not the current instance, threads can still be synchronized correctly.

public class AccountingSync implements Runnable {
  static int i = 0;
  public static synchronized void increase() {
    i++;
  }
  
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      increase();
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(new AccountingSync());
    Thread t2 = new Thread(new AccountingSync());
  
    t1.start();
    t2.start();
  
    t1.join();
    t2.join();
  
    System.out.println(i);
  }
}
Copy after login

3. Wrong locking

From the above example, we know that if we need a counter application, in order to ensure the correctness of the data, we will naturally need to lock the counter. Therefore, we may The following code will be written:

public class BadLockOnInteger implements Runnable {
  static Integer i = 0;
  @Override
  public void run() {
    for (int j = 0; j < 1000000; j++) {
      synchronized (i) {
        i++;
      }
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    BadLockOnInteger badLockOnInteger = new BadLockOnInteger();
  
    Thread t1 = new Thread(badLockOnInteger);
    Thread t2 = new Thread(badLockOnInteger);
  
    t1.start();
    t2.start();
  
    t1.join();
    t2.join();
  
    System.out.println(i);
  }
}
Copy after login

When we run the above code, we will find that the output i is very small. This shows that the thread is not safe.

To explain this problem, we have to start with Integer: In Java, Integer is an immutable object. Like String, once the object is created, it cannot be modified. If you have an Integer=1, then It will always be 1. What if you want this object = 2? You can only recreate an Integer. After each i++, it is equivalent to calling the valueOf method of Integer. Let's take a look at the source code of the valueOf method of Integer:

public static Integer valueOf(int i) {
  if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
  return new Integer(i);
}
Copy after login

## Integer.valueOf() is actually a factory method. It will tend to return a new Integer object and re-copy the value to i;


So, we know the cause of the problem. Since among multiple threads, after i++, i points to a new object, so the thread may load a different object each time it locks. Above the object instance. The solution is very simple. Use one of the three synchronize methods above to solve the problem.


For more articles related to the usage of the java synchronized keyword, please Follow PHP Chinese website!


Related labels:
source:php.cn
Statement of this Website
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
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template