Home  >  Article  >  Java  >  Introduction to several methods of Java multi-thread synchronization

Introduction to several methods of Java multi-thread synchronization

黄舟
黄舟Original
2017-09-20 10:04:061569browse

This article mainly introduces relevant information on several methods of synchronization of java multi-threads. Here are 5 methods. Friends in need can refer to it

Several methods of synchronization of java multi-threads

1. Introduction

##1. Without further ado, let’s get to the point.

2. Why thread synchronization is needed

Because when we have multiple threads accessing a variable or object at the same time, if there are readers in these threads, When there is another write operation, the variable value or the state of the object will be confused, resulting in program exception. For example, if a bank account is operated by two threads at the same time, one withdraws 100 yuan and the other deposits 100 yuan. Assume that the account originally has 0 blocks. If the withdrawal thread and the deposit thread occur at the same time, what will happen? The money withdrawal is unsuccessful, and the account balance is 100. The money withdrawal is successful, and the account balance is 0. So which one is it? It's hard to tell. Therefore, multi-thread synchronization is to solve this problem.

3. Code when not synchronized


Bank.java


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public void addMoney(int money){ 
    count +=money; 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public void subMoney(int money){ 
    if(count-money < 0){ 
      System.out.println("余额不足"); 
      return; 
    } 
    count -=money; 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

SyncThreadTest.java


package threadTest; 
/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class SyncThreadTest { 

  public static void main(String args[]){ 
    final Bank bank=new Bank(); 

    Thread tadd=new Thread(new Runnable() { 

      @Override 
      public void run() { 
        // TODO Auto-generated method stub 
        while(true){ 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          } 
          bank.addMoney(100); 
          bank.lookMoney(); 
          System.out.println("\n"); 

        } 
      } 
    }); 

    Thread tsub = new Thread(new Runnable() { 

      @Override 
      public void run() { 
        // TODO Auto-generated method stub 
        while(true){ 
          bank.subMoney(100); 
          bank.lookMoney(); 
          System.out.println("\n"); 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          }   
        } 
      } 
    }); 
    tsub.start(); 

    tadd.start(); 
  } 

}

The code is very simple, I won’t explain it, let’s see what the results are like? I've cut out part of it. It's very messy, and I can't understand it.


余额不足 
账户余额:0 

余额不足 
账户余额:100 

1441790503354存进:100 
账户余额:100 

1441790504354存进:100 
账户余额:100 

1441790504354取出:100 
账户余额:100 

1441790505355存进:100 
账户余额:100 

1441790505355取出:100 
账户余额:100

4. Code when using synchronization


(1) Synchronization method:


That is, there is a method modified by the synchronized keyword. Since every object in Java has a built-in lock, when a method is modified with this keyword, the built-in lock will protect the entire method. Before calling this method, you need to obtain the built-in lock, otherwise it will be blocked.

Modified Bank.java


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public synchronized void addMoney(int money){ 
    count +=money; 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public synchronized void subMoney(int money){ 
    if(count-money < 0){ 
      System.out.println("余额不足"); 
      return; 
    } 
    count -=money; 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

Look at the running results again:


余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441790837380存进:100 
账户余额:100 

1441790838380取出:100 
账户余额:0 
1441790838380存进:100 
账户余额:100 

1441790839381取出:100 
账户余额:0

It feels OK instantly Got it.

Note: The synchronized keyword can also modify static methods. If the static method is called at this time, the entire class will be locked

(2) Synchronized code block


That is, the statement block modified by the synchronized keyword. The statement block modified by this keyword will automatically be added with a built-in lock to achieve synchronization

Bank.java code is as follows:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public  void addMoney(int money){ 

    synchronized (this) { 
      count +=money; 
    } 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public  void subMoney(int money){ 

    synchronized (this) { 
      if(count-money < 0){ 
        System.out.println("余额不足"); 
        return; 
      } 
      count -=money; 
    } 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

The running results are as follows:


余额不足 
账户余额:0 

1441791806699存进:100 
账户余额:100 

1441791806700取出:100 
账户余额:0 

1441791807699存进:100 
账户余额:100

The effect is similar to method one.

Note: Synchronization is a high-cost operation, so the content of synchronization should be minimized. Usually there is no need to synchronize the entire method, just use synchronized code blocks to synchronize key code.

(3) Use special domain variables (Volatile) to achieve thread synchronization

a. The volatile keyword provides a lock-free mechanism for access to domain variables b. Using volatile to modify the domain is equivalent to Tell the virtual machine that this field may be updated by other threads c. Therefore, each time the field is used, it must be recalculated instead of using the value in the register. Volatile does not provide any atomic operations, nor can it be used to modify final types. The variable


Bank.java code is as follows:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private volatile int count = 0;// 账户余额 

  // 存钱 
  public void addMoney(int money) { 

    count += money; 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 
  } 

  // 取钱 
  public void subMoney(int money) { 

    if (count - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count -= money; 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count); 
  } 
}

What is the running effect?

余额不足 
账户余额:0 

余额不足 
账户余额:100 

1441792010959存进:100 
账户余额:100 

1441792011960取出:100 
账户余额:0 

1441792011961存进:100 
账户余额:100

Are you confused again? Why is this? This is because volatile cannot guarantee atomic operations, so volatile cannot replace synchronized. In addition, volatile will prevent the compiler from optimizing the code, so if you can't use it, don't apply it. Its principle is that every time a thread wants to access a volatile-modified variable, it reads it from the memory instead of reading it from the cache, so the variable value accessed by each thread is the same. This ensures synchronization.

(4) Use reentrancy locks to achieve thread synchronization

A new java.util.concurrent package has been added in JavaSE5.0 to support synchronization. The ReentrantLock class is a reentrant, mutually exclusive lock that implements the Lock interface. It has the same basic behavior and semantics as using synchronized methods and blocks, and extends its capabilities. Commonly used methods of the ReentrantLock class are: ReentrantLock(): Create a ReentrantLock instance lock(): Obtain the lock unlock(): Release the lock Note: ReentrantLock() also has a constructor that can create a fair lock, but because it can greatly reduce the program For operating efficiency, it is not recommended to use Bank.java code modification as follows:


package threadTest; 

import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count = 0;// 账户余额 

  //需要声明这个锁 
  private Lock lock = new ReentrantLock(); 

  // 存钱 
  public void addMoney(int money) { 
    lock.lock();//上锁 
    try{ 
    count += money; 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 

    }finally{ 
      lock.unlock();//解锁 
    } 
  } 

  // 取钱 
  public void subMoney(int money) { 
    lock.lock(); 
    try{ 

    if (count - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count -= money; 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
    }finally{ 
      lock.unlock(); 
    } 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count); 
  } 
}

What is the operating effect?


余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441792891934存进:100 
账户余额:100 

1441792892935存进:100 
账户余额:200 

1441792892954取出:100 
账户余额:100

The effect is similar to the first two methods.

If the synchronized keyword can meet the needs of users, use synchronized because it can simplify the code. If you need more advanced functions, use the ReentrantLock class. At this time, pay attention to releasing the lock in time, otherwise a deadlock will occur. The lock is usually released in the finally code

(5) Use local variables to achieve thread synchronization

Bank.java code is as follows:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){ 

    @Override 
    protected Integer initialValue() { 
      // TODO Auto-generated method stub 
      return 0; 
    } 

  }; 

  // 存钱 
  public void addMoney(int money) { 
    count.set(count.get()+money); 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 

  } 

  // 取钱 
  public void subMoney(int money) { 
    if (count.get() - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count.set(count.get()- money); 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count.get()); 
  } 
}

Running effect:


###
余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441794247939存进:100 
账户余额:100 

余额不足 
1441794248940存进:100 
账户余额:0 

账户余额:200 

余额不足 
账户余额:0 

1441794249941存进:100 
账户余额:300
### After seeing the running effect, I was confused at first, why only deposits are allowed but not withdrawals? Take a look at the principle of ThreadLocal: ######

If you use ThreadLocal to manage variables, each thread that uses the variable will obtain a copy of the variable. The copies are independent of each other, so that each thread can modify its own copy of the variable at will without causing any harm to other threads. Influence. Now you understand, it turns out that each thread runs a copy, which means that depositing money and withdrawing money are two accounts with the same knowledge name. So the above effect will occur.

ThreadLocal and synchronization mechanism

a. ThreadLocal and synchronization mechanism are both to solve the problem of access conflicts of the same variables in multiple threads b. The former uses "space for time" "method, the latter adopts the method of "exchanging time for space"

The above is the detailed content of Introduction to several methods of Java multi-thread synchronization. For more information, please follow other related articles on the PHP Chinese website!

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