catalogue
Thread synchronization
-
Multiple threads operate on the same resource
-
Concurrency: the same object is operated by multiple threads at the same time
-
Thread synchronization is actually a waiting mechanism. Multiple threads that need to access this object at the same time enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it for the next thread
-
Forming condition: queue + lock -- > solves thread safety problems
-
For example, queue up to go to the bathroom, lock the door after one goes in, recognize that the lock is open, and the next can go in again
-
Performance inversion is easy to occur after locking, that is, threads with low priority get the lock first, resulting in threads with high priority unable to execute
-
Only the contents that need to be modified need to be locked. Too many locks will cause a waste of resources
-
Synchronization method
synchronized methods control access to "objects". Each object corresponds to a lock. Each synchronize d method must obtain the lock of the object calling the method before execution. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock until the method returns. The blocked thread can obtain the lock and continue to execute.
//The ticket grabbing method is not synchronized with the synchronize keyword, and the thread is unsafe. The number of tickets sold is greater than the actual number, that is, the remaining tickets are negative or different people grab the same ticket public class StationBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"Zhang San").start(); new Thread(buyTicket,"Li Si").start(); new Thread(buyTicket,"Wang Wu").start(); } } class BuyTicket implements Runnable{ private int ticketNums = 10;//Number of votes private boolean flag = true;//External thread stop mode flag bit @Override public void run() { while(flag){ buy(); } } //In the synchronized synchronization method, the default lock is this itself, that is, BuyTicket private synchronized void buy(){ if (ticketNums <= 0){//The tickets were robbed flag = false; return; }else { try { Thread.sleep(100);//sleep simulates the delay, which can amplify the occurrence of the problem } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "Got the third" + ticketNums-- + "Ticket"); } } }
Synchronization block
-
Synchronization block: synchronize(Obj) {}
-
Obj is called synchronization monitor
-
It can be any object. It is recommended to use shared resources
-
There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this or class
-
-
Synchronization monitor execution process
-
The first thread accesses, locks the synchronization monitor, and executes the code in it
-
The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
-
After the first thread is executed, unlock the synchronization monitor
-
The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses
-
public class BankDrawMoney { public static void main(String[] args) { //Create bank account Account account = new Account("Marriage fund",1000); //Withdrawal object WithdrawMoney you = new WithdrawMoney(account,50,"you"); WithdrawMoney girlFriend = new WithdrawMoney(account,100,"girl friend"); you.start(); girlFriend.start(); } } //account class Account{ private String name;//Account name private int money;//balance public Account(String name, int money){ this.money = money; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } //Bank: simulated withdrawal class WithdrawMoney extends Thread{ Account account;//account int drawingMoney;//How much did you withdraw int nowMoney;//How much money do you have now public WithdrawMoney(Account account, int drawingMoney, String name){ super(name);//Call the parameterized constructor of the parent class this.account = account; this.drawingMoney = drawingMoney; } //Withdraw money @Override public void run() { //The amount of lock is the amount of change, that is, the amount that needs to be added, deleted and modified synchronized (account){ //First, judge whether there is money in the account if(account.getMoney() - drawingMoney < 0){ System.out.println(Thread.currentThread().getName() + "Insufficient balance, unable to withdraw!"); return; } try { Thread.sleep(1000);//sleep amplification problem } catch (InterruptedException e) { e.printStackTrace(); } //Card balance account.setMoney(account.getMoney() - drawingMoney); //Money in hand nowMoney = nowMoney + drawingMoney; System.out.println(Thread.currentThread().getName() + "Money in hand:" + nowMoney); System.out.println(account.getName() + "The account balance is:" + account.getMoney()); } } }
deadlock
Multiple threads occupy some shared resources and wait for the resources occupied by other threads to continue execution. As a result, two or more threads are waiting for each other to release resources and stop execution.
When a synchronization block has "locks of more than two objects" at the same time, the problem of "deadlock" may occur.
Necessary conditions for deadlock generation:
-
Mutex condition: a resource can only be used by one process at a time.
-
Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
-
Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up
-
Cyclic waiting condition: a head to tail cyclic waiting resource relationship is formed between several processes.
As long as one or more of the above four conditions are solved, the deadlock problem can be avoided
//Deadlock: multiple threads hold each other's required resources, forming a deadlock public class DeadLock { public static void main(String[] args) { Makeup girl1 = new Makeup(0,"Marry"); Makeup girl2 = new Makeup(1,"Lily"); girl1.start(); girl2.start(); //At this time, Lily obtains the lipstick lock and Mary obtains the mirror lock. Both of them occupy the shared resources, resulting in a deadlock and the program cannot continue to execute //Solution: after everyone gets the resource, release it first, and then wait for the second resource, that is, it is not written in the way of nested synchronization blocks } } //Lipstick class Lipstick{ } //mirror class Mirror{ } //Make up class Makeup extends Thread{ //Existing resources: a mirror, a lipstick static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//Selection String girlName;//Makeup man public Makeup(int choice, String girlName){ this.choice = choice; this.girlName = girlName; } @Override public void run() { try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //Make up and hold each other's locks private void makeup() throws InterruptedException { if (choice == 0){//Take the mirror first synchronized (mirror){//Get mirror lock System.out.println(this.girlName + "Got the mirror lock"); Thread.sleep(1000); synchronized (lipstick){//Lock the mirror and wait for the lipstick lock System.out.println(this.girlName + "Obtained lipstick lock"); } } }else if (choice == 1){//Get lipstick first synchronized (lipstick){//Get lipstick lock System.out.println(this.girlName + "Obtained lipstick lock"); Thread.sleep(2000); synchronized (mirror){//Hold the lipstick lock and wait for the mirror lock System.out.println(this.girlName + "Got the mirror lock"); } } } } }
Lock lock
-
Thread synchronization mechanism -- synchronization is achieved by displaying and defining the synchronization Lock object, which uses the Lock object as the synchronization Lock.
-
The java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Threads should obtain the Lock object before accessing shared resources.
-
ReentrantLock class implements Lock and has the same concurrency and memory semantics as synchronize. ReentrantLock is commonly used to display locking and releasing locks.
Use form
class A{ private final ReentrantLock lock = new ReentrantLock(); public void function(){ try { lock.lock(); //Thread safe code; }finally { lock.unlock(); //If there is an exception in the synchronization code, write unlock() to the finally statement block } } }
import java.util.concurrent.locks.ReentrantLock; //Thread safety control of ticket buying is realized through Lock lock public class Lock { public static void main(String[] args) { TestReentrantLock reentrantLock = new TestReentrantLock(); new Thread(reentrantLock).start(); new Thread(reentrantLock).start(); new Thread(reentrantLock).start(); } } class TestReentrantLock implements Runnable{ int ticketNums = 10; //Define lock lock private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock();//Lock if (ticketNums <= 0){ break; }else { Thread.sleep(1000); System.out.println(ticketNums--); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//Unlock after use } } } }
synchronize vs Lock
-
Lock is an explicit lock (which can be opened and closed manually. Don't forget to close the lock), and synchronize is an implicit lock, which is automatically released out of the scope.
-
Lock only has code block lock, and synchronize has code block lock and method lock.
-
Using Lock lock JVM will take less time to schedule threads and perform better. And it has better scalability (providing more subclasses).
-
Priority:
-
Lock > synchronize code block (it has entered the method body and allocated corresponding resources) > synchronize method (outside the method body)
-
Thread pool
Create multiple threads in advance, put them into the thread pool, obtain them directly when using them, and put them back into the thread pool after using them. It can avoid frequent creation and destruction and realize reuse.
Benefits:
-
Increase the corresponding speed (reduce the time for new threads)
-
Reduce resource consumption (you don't need to create threads every time)
-
Easy thread management
-
corePoolSize: the size of the core pool
-
maximumPoolSize: maximum number of threads
-
keepAliveTime: when the thread has no task, how long will it be terminated after holding it for at most
-
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestPool { public static void main(String[] args) { //Create service, create thread pool //Real thread pool interface //Executors tool class and thread pool factory class are used to create and return different types of thread pools ExecutorService service = Executors.newFixedThreadPool(10);//The parameter is the thread pool size //Execute executes tasks / commands. It is generally used to execute Runnable //submit has a return value. The return value type is future < T >, which is generally used to execute Callable service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); //Close connection pool service.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }