To understand this problem, we must first know what is synchronized.
In a multithreaded environment, there will be some problems when multiple threads access shared resources at the same time, and the synchronized keyword is used to ensure thread synchronization.
Let's look at a case first
For these 10 threads, we want them to implement. For each thread, I want them to accumulate 9999999 times. The result should be 10 * 9999999. However, according to the actual situation, it is not the result, and the results are inconsistent every time you run. You can try it yourself. It's worth thinking. Why???
synchronized
Let's discuss the security of shared data: When a thread is running, it will have its own stack space and run on its own stack Space running, if there is no shared data between multiple threads, that is To put it bluntly, if multithreads do not work together to accomplish one thing, the existence of multithreads is meaningless, but if there is shared data, how should we deal with the thread safety of shared data?????? Let's have a deep discussion here. We naturally think of it along the way Let each thread read and write the shared variable in turn, so that there will be no problem of data security. Because each thread operates on the latest version data. So, stay java keyword synchronized It has the function of queuing each thread in turn The ability to manipulate shared variables. Obviously, this synchronization mechanism is inefficient, but synchronized It is the basis of other concurrent container implementations The solution will also greatly improve the feeling of concurrent programming. From a utilitarian point of view, This is also the test site of high frequency interview. Well, let's talk about this in detail keyword.
package day30.teacher; public class SellTicketDemo1 implements Runnable { private static int count = 0; public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new SellTicketDemo1()); thread.start(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("result: " + count); } @Override public void run() { for (int i = 0; i < 9999999; i++) count++; } }
Implementation principle
According to the location where synchronized is used, there can be the following usage scenarios: it can be seen that synchronized can be used in methods and code blocks, which are described one by one here.
- When used in methods, instance methods and static methods are instance objects and objects of this class respectively.
- The code block can be divided into three types. However, if the locked class is an object, although new has multiple instance objects, they still belong to one class and the same class will still be locked, that is, the synchronization relationship between threads is guaranteed.
So three questions: 1,Who is the lock object of the synchronization code block? Any object, but the lock object between multiple threads should be the same 2,Synchronization method? take synchronized Keyword on method The lock object of the synchronization method is this 3,Who is the lock object of the static method? class File, bytecode file object also belongs to a Object Class (said when reflecting on the last day) this class The file cannot be a bytecode file of any class Should be run Bytecode file of the class where the method is located
monitor mechanism
The synchronous code block here locks the class object, and there is also a synchronous static method, which still locks the class object of the class.
public class Monitor1 { public static void main(String[] args) { synchronized (Monitor1.class) { } method(); } private static void method() { } }
in use Synchronized The key to synchronization is to synchronize the objects monitor monitor Get when the thread gets monitor Only after Continue to execute, otherwise you can only wait. The acquisition process is mutually exclusive , that is, only one thread can get it at the same time monitor. above of demo After executing the synchronization code block, we will execute another synchronization code block Static synchronization method, and the object locked by this method is still this class object, Does the executing thread still need to obtain the lock? The answer is No of When you execute a static synchronization method, there is only one monitorexit finger No, No monitorenter Instruction to acquire a lock. This is the weight of the lock Accessibility, that is, in the same lock process, the thread does not need to acquire the same lock again. Synchronized Congenital reentrant. Each object has a count After the thread obtains the lock of the object, the counter will be incremented by one, and after the lock is released The counter will be decremented by one. Any object has its own monitor when the object consists of a synchronization block Or when the synchronous method of this object is called, the thread executing the method must first Get the monitor of the object to enter the synchronization block and synchronization method. If not The thread that gets the monitor will be blocked in the synchronization block and the synchronization method Entrance to BLOCKED Status (you can see the status of the thread
To access an Object, any thread must first obtain the monitor of the Object. If the acquisition fails, the thread enters the synchronization state and the thread state changes to BLOCKED. When the monitor owner of the Object is released, the thread in the synchronization queue will have the opportunity to re acquire the monitor.
Advantages and disadvantages of synchronization
Benefits of synchronization: It solves the security problem of multithreading Disadvantages of synchronization: After adding a thread, it is equivalent to adding a lock. You will judge it every time you enter the synchronous code block Virtually, it reduces the execution efficiency
Case:
public class TicketWindow1 implements Runnable{ //Define 100 tickets private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true){ synchronized (obj) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + (tickets--) + "Ticket"); } } } } } public class SellTicketDemo1 { public static void main(String[] args) { TicketWindow1 ticketWindow1 = new TicketWindow1(); //Create three thread objects to simulate three windows //Thread(Runnable target, String name) //Assign a new Thread object. //While creating the thread object, give the thread a name Thread t1 = new Thread(ticketWindow1,"Window 1"); Thread t2 = new Thread(ticketWindow1,"Window 2"); Thread t3 = new Thread(ticketWindow1,"Window 3"); t1.start(); t2.start(); t3.start(); } }
Lock
concept
In fact, the problem of thread synchronization security is not only solved synchronized This One method, we also have the second method, that is, locking Lock
The synchronized keyword is introduced above. Through analysis, which code blocks are packaged, but we don't directly see where the Lock is locked or where other threads are released. In order to more clearly express how to Lock and release the Lock, jdk1 After 5, a new Lock object Lock is provided.
method
By looking at the API, we found that Lock is an interface, so we must implement this interface through its subclasses.
Specific subclasses: Class ReentrantLock void lock() Lock void unlock() Release lock
public class TicketWindow3 implements Runnable { //Define 100 tickets private int tickets = 100; //Create lock object Lock lock = new ReentrantLock(); @Override public void run() { while (true){ //t1,t2,t3 lock.lock(); if (tickets > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + (tickets--) + "Ticket"); } lock.unlock(); } } } public class SellTicketDemo3 { public static void main(String[] args) { TicketWindow3 ticketWindow3 = new TicketWindow3(); //Create three thread objects to simulate three windows //Thread(Runnable target, String name) //Assign a new Thread object. //While creating the thread object, give the thread a name Thread t1 = new Thread(ticketWindow3,"Window 1"); Thread t2 = new Thread(ticketWindow3,"Window 2"); Thread t3 = new Thread(ticketWindow3,"Window 3"); t1.start(); t2.start(); t3.start(); } }