In multi-threaded programming, the most critical and most concerned issue should be the synchronization issue, which is a difficult point and the core.
From the synchronized and volatile of the earliest version of jdk, to the Lock interface in the java.util.concurrent.locks package provided in jdk 1.5 (the implementations include ReadLock, WriteLock, and ReentrantLock), the multi-threading implementation is also gradually maturing.
What mechanism is used to control synchronization? The first reaction is locking, which should have been exposed to when learning the operating system and the database. In Java multi-threaded programs, when multiple programs compete for the same resource, in order to prevent resource corrosion, the first thread accessing the resource is assigned an object lock, and the later generations need to wait for the release of this object lock.
Yes, the most concerned thing about the synchronization of Java threads is the use of shared resources.
Let’s first understand some shared resources of which threads are available.
From the JVM, we need to coordinate the data shared by threads:
1. The instance variable saved in the heap; 2. The class variable saved in the method area.
When the Java virtual machine loads a class, each object or class will be associated with a monitor to protect the object's instance variable or class variable; of course, if the object has no instance variables, or the class has no variables, the monitor will monitor nothing.
In order to achieve the mutex of monitors mentioned above, the virtual machine associates a lock (also called an invisible lock) for each object or class. Let me explain here that class locks are also implemented through object locks, because when the class is loaded, the JVM will create an instance of java.lang.Class for each class; so when the lock is against an object, the class object of this class is locked.
In addition, a thread can lock an object multiple times, which corresponds to multiple releases; it is a lock calculator provided by JVM for each object lock. The last lock is added 1, and the corresponding minus 1, and when the calculator value is 0, it is released. This object lock is used by the monitor inside the JVM and is also automatically generated by the JVM. All programmers don't need to add it by themselves.
After introducing the synchronization principle of Java, we will get to the topic and first talk about the use of synchronized. Other synchronizations will be introduced in the following chapters.
Let's try running an example first.
package thread_test; /** * Test multithreaded programs that extend the implementation of Thread class* */ public class TestThread extends Thread{ private int threadnum; public TestThread(int threadnum) { this.threadnum = threadnum; } @Override public synchronized void run() { for(int i = 0;i<1000;i++){ System.out.println("NO." + threadnum + ":" + i ); } } public static void main(String[] args) throws Exception { for(int i=0; i<10; i++){ new TestThread(i).start(); Thread.sleep(1); } } }
Running results:
NO.0:887 NO.0:888 NO.0:889 NO.0:890 NO.0:891 NO.0:892 NO.0:893 NO.0:894 NO.7:122 NO.7:123 NO.7:124
The above is just a clip, explaining a problem.
If you are careful, you will find that NO.0:894 is followed by NO.7:122, which means that it does not start from 0 to 999.
It is said that synchronized can implement synchronization methods or synchronization blocks, why can't it work here?
Let’s first analyze the synchronization mechanism. Synchronization is achieved through locking. So in the above example, what object is locked or what class is locked? There are two variables inside, one is i and the other is threadnum; i is internal to the method, and threadnum is private.
Let’s learn about the running mechanism of synchronized:
In a Java program, when synchronized block or synchronized method is used, this area is marked for monitoring; while when a JVM handles the program, when a program enters the monitoring area, it will automatically lock the object or class.
So in the above example, what is locked after the synchronized keyword is used?
When synchronized method, lock the instance object that calls the method itself as the object lock. In this example, the 10 threads have their own TestThread class objects, so the object lock acquired is also its own object lock and has nothing to do with other threads.
To implement method locking, shared objects must be locked.
Change the above example and then take a look:
package thread_test; /** * Test multithreaded programs that extend the implementation of Thread class* */ public class TestThread extends Thread{ private int threadnum; private String flag; // Mark public TestThread(int threadnum,String flag) { this.threadnum = threadnum; this.flag = flag; } @Override public void run() { synchronized(flag){ for(int i = 0;i<1000;i++){ System.out.println("NO." + threadnum + ":" + i ); } } } public static void main(String[] args) throws Exception { String flag = new String("flag"); for(int i=0; i<10; i++){ new TestThread(i,flag).start(); Thread.sleep(1); } } }
This is also added a shared flag. Then, the flag flag is synchronized through the synchronized block; this meets the conditions for locking the shared object.
Yes, the running results have come in order.
Through the synchronized block, specify the acquisition of object locks to achieve synchronization. So are there any other methods that can be implemented through the synchronized method?
According to the principle of synchronization: if a shared object lock or class lock can be obtained, synchronization can be achieved. So can we achieve it by sharing a class lock?
Yes, we can use static synchronization methods. According to the characteristics of static methods, it only allows the class object itself to be called, and cannot be called by instantiating a class object. Then if you obtain the lock of this static method, you will obtain the class lock, and this class lock is all TestThread class locks, and the purpose of obtaining the shared class locks is achieved.
The implementation code is as follows:
package thread_test; /** * Test multi-threaded programs that extend the implementation of Thread class* * @author ciding * @createTime Dec 7, 2011 9:37:25 AM * */ public class TestThread extends Thread{ private int threadnum; public TestThread(int threadnum) { this.threadnum = threadnum; } public static synchronized void staticTest(int threadnum) { for(int i = 0;i<1000;i++){ System.out.println("NO." + threadnum + ":" + i ); } } public static void main(String[] args) throws Exception { for(int i=0; i<10; i++){ new TestThread(i).start(); Thread.sleep(1); } } @Override public void run(){ staticTest(threadnum); } } The run result is omitted, the same as in the second example.
The above content mainly explains two issues: synchronization blocks and synchronization methods.
1. Synchronized block: The acquired object lock is the flag object lock in synchronized (flag).
2. Synchronization method: The class object to which the method belongs, and the class object lock.
The static synchronization method will definitely be synchronized since multiple threads will be shared.
Instead of static synchronization methods, they will only be synchronized in singleton mode.