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
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
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.
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 账户余额:0It 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
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
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 账户余额:100The 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.
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
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 账户余额:100The 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
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!