Java learning notes-five methods of multi-thread synchronization, java learning notes
I. Introduction
A few days ago, the interview was abused by the master and a lot of basic knowledge had to be taken up again. If you don't talk much about it, go to the topic.
Ii. Why thread synchronization?
Because when we have multiple threads that want to access a variable or object at the same time, if there are both read and write operations in these threads, it will lead to confusion of the variable value or object status, this causes program exceptions. For example, if a bank account is operated by two threads at the same time, one takes 100 yuan and the other saves 100 yuan. Assume that there are 0 accounts. What will happen if both the money withdrawal thread and the deposit thread occur? Fail to get the money. The account balance is 100. The money is successfully obtained, and the account balance is 0. Which one is it? It is hard to say clearly. Therefore, multithreading synchronization is to solve this problem.
3. Code for different steps
Bank. java
Package threadTest;/*** @ author ww **/public class Bank {private int count = 0; // account balance // Save public void addMoney (int money) {count + = money; System. out. println (System. currentTimeMillis () + "deposit:" + money);} // get the public void subMoney (int money) {if (count-money <0) {System. out. println ("insufficient balance"); return;} count-= money; System. out. println (+ System. currentTimeMillis () + "Fetch:" + money);} // query public void lookMoney () {System. out. println ("account balance:" + count );}}
SyncThreadTest. java
package threadTest;public class SyncThreadTest {public static void main(String args[]){final Bank bank=new Bank();Thread tadd=new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubwhile(true){try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}bank.addMoney(100);bank.lookMoney();System.out.println("\n");}}});Thread tsub = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubwhile(true){bank.subMoney(100);bank.lookMoney();System.out.println("\n");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}});tsub.start();tadd.start();}}
The code is very simple. I will not explain it. What is the running result? If some of them are intercepted, is it messy.
Balance insufficient account balance: 0 balance insufficient account balance: 1001441790503354 deposit: 100 account balance: 1001441790504354 deposit: 100 account balance: 1001441790504354 withdrawal: 100 account balance: 1001441790505355 deposit: 100 account balance: 1001441790505355 withdrawal: 100 account balance: 100
4. Use the synchronization code (1) synchronization method:
There is also a method for modifying the synchronized keyword. Because every object in java has a built-in lock, when this keyword is used to modify the method, the built-in lock will protect the entire method. You must obtain the built-in lock before calling this method. Otherwise, the lock is blocked.
Modified Bank. java
Package threadTest;/*** @ author ww **/public class Bank {private int count = 0; // account balance // Save public synchronized void addMoney (int money) {count + = money; System. out. println (System. currentTimeMillis () + "deposit:" + money);} // get the money public synchronized void subMoney (int money) {if (count-money <0) {System. out. println ("insufficient balance"); return;} count-= money; System. out. println (+ System. currentTimeMillis () + "Fetch:" + money);} // query public void lookMoney () {System. out. println ("account balance:" + count );}}Let's look at the running result:
Balance insufficient account balance: 0 balance insufficient account balance: 01441790837380 deposit: 100 account balance: 1001441790838380 withdrawal: 100 account balance: 01441790838380 deposit: 100 account balance: 1001441790839381 withdrawal: 100 account balance: 0
I can understand it in an instant.
Note: The synchronized keyword can also modify the static method. If you call this static method, the entire class will be locked.
(2) Synchronous Code Block
There are statement blocks modified by the synchronized keyword. The statement block modified by this keyword is automatically added with a built-in lock to achieve synchronization.
The Bank. java code is as follows:
Package threadTest;/*** @ author ww **/public class Bank {private int count = 0; // account balance // Save public void addMoney (int money) {synchronized (this) {count + = money;} System. out. println (System. currentTimeMillis () + "deposit:" + money);} // get the money public void subMoney (int money) {synchronized (this) {if (count-money <0) {System. out. println ("insufficient balance"); return;} count-= money;} System. out. println (+ System. currentTimeMillis () + "Fetch:" + money);} // query public void lookMoney () {System. out. println ("account balance:" + count );}}
The running result is as follows:
Insufficient balance account balance: 01441791806699 deposit: 100 account balance: 1001441791806700 withdrawal: 100 account balance: 01441791807699 deposit: 100 account balance: 100
The effect is similar to method 1.
Note: synchronization is a highly open operation, so you should minimize the amount of synchronization content. Generally, there is no need to synchronize the entire method. Use the synchronized code block to synchronize key code.
(3) use special domain variables (volatile) for Thread Synchronization
A. The volatile keyword provides a lock-free mechanism for access to domain variables.
B. Modifying the domain using volatile is equivalent to telling the virtual machine that the domain may be updated by other threads.
C. Therefore, each time you use this field, you need to recalculate it instead of using the value in the register.
D. volatile does not provide any atomic operations, nor can it be used to modify final-type variables.
The Bank. java code is as follows:
Package threadTest;/*** @ author ww **/public class Bank {private volatile int count = 0; // account balance // Save public void addMoney (int money) {count + = money; System. out. println (System. currentTimeMillis () + "deposit:" + money);} // get the public void subMoney (int money) {if (count-money <0) {System. out. println ("insufficient balance"); return;} count-= money; System. out. println (+ System. currentTimeMillis () + "Fetch:" + money);} // query public void lookMoney () {System. out. println ("account balance:" + count );}}What is the running effect?
Balance insufficient account balance: 0 balance insufficient account balance: 1001441792010959 deposit: 100 account balance: 1001441792011960 withdrawal: 100 account balance: 01441792011961 deposit: 100 account balance: 100
Are you confused. Why? Because volatile cannot guarantee atomic operations, volatile cannot replace synchronized. In addition, volatile will organize the compiler to optimize the code, so it is not applicable if it is not used. The principle is that every time a thread wants to access a variable modified by volatile, it reads data from the memory instead of the memory cache. Therefore, the variable values accessed by each thread are the same. This ensures synchronization.
(4) use the re-entry lock for Thread Synchronization
Added a java. util. concurrent package in JavaSE5.0 to support synchronization. The ReentrantLock class is a reentrant and mutex Lock that implements the Lock interface. It has the same basic behavior and semantics as using the synchronized Method and fast, and extends its capabilities.
Common ReenreantLock methods include:
ReentrantLock (): Creates a ReentrantLock instance.
Lock (): Get the lock
Unlock (): Release the lock
Note: ReentrantLock () also has a constructor that can create a fair lock. However, it is not recommended because it can greatly reduce the program running efficiency.
The Bank. java code is modified 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; // account balance // you need to declare this Lock private lock = new ReentrantLock (); // Save public void addMoney (int money) {Lock. lock (); // lock try {count + = money; System. out. println (System. currentTimeMillis () + "saved in:" + money);} finally {lock. unlock (); // unlock} // get the public void subMoney (int money) {lock. lock (); try {if (count-money <0) {System. out. println ("insufficient balance"); return;} count-= money; System. out. println (+ System. currentTimeMillis () + "Fetch:" + money);} finally {lock. unlock () ;}// query public void lookMoney () {System. out. println ("account balance:" + count );}}How is the running effect?
Balance insufficient account balance: 0 balance insufficient account balance: 01441792891934 deposit: 100 account balance: 1001441792892935 deposit: 100 account balance: 2001441792892954 withdrawal: 100 account balance: 100
The effect is similar to the previous two methods.
If the synchronized keyword can meet your needs, use synchronized because it simplifies code. If you need more advanced functions, use the ReentrantLock class. Pay attention to releasing the lock in time. Otherwise, a deadlock occurs. Usually, the lock is released in finally code.
(5) use local variables for Thread Synchronization
The Bank. java code is as follows:
Package threadTest;/*** @ author ww **/public class Bank {private static ThreadLocal <Integer> count = new ThreadLocal <Integer> () {@ Overrideprotected Integer initialValue () {// TODO Auto-generated method stubreturn 0 ;}}; // Save public void addMoney (int money) {count. set (count. get () + money); System. out. println (System. currentTimeMillis () + "deposit:" + money);} // get the public void subMoney (int money) {if (count. get ()-money <0) {System. out. println ("insufficient balance"); return;} count. set (count. get ()-money); System. out. println (+ System. currentTimeMillis () + "Fetch:" + money);} // query public void lookMoney () {System. out. println ("account balance:" + count. get ());}}
Running effect:
Insufficient balance account balance: 0 insufficient balance account balance: 01441794247939 deposit: 100 account balance: 100 insufficient balance 1441794248940 deposit: 100 account balance: 0 account balance: 200 insufficient balance account balance: 01441794249941 deposit: 100 account balance: 300
After reading the running effect, I was confused at the beginning. How can I just save it? Let's take a look at the principle of ThreadLocal:
If ThreadLocal is used to manage a variable, each thread that uses the variable obtains 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, it does not affect other threads. Now, I understand that each thread runs a copy. That is to say, there are two accounts for saving and obtaining money, and the same knowledge name is used. So the above effect will occur.
ThreadLocal and synchronization mechanism
A. The ThreadLocal and synchronization mechanisms are designed to solve the access conflicts of the same variables in multiple threads.
B. The former adopts the "space for Time" method, and the latter adopts the "Time for space" method. Now I understand it. Each has its own advantages and disadvantages and has its own applicable scenarios. Manual, eat now.
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.