Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
In the last article “Java multi-thread (three) multi-thread insecurity typical example” said the problem of multi-thread insecurity and three typical examples, in this article explains a way to ensure multi-thread security — synchronized keyword.
The use of synchronized
Synchronized is equivalent to locking objects or classes to prevent other threads from accessing shared resources, thereby protecting the security of multiple threads. The principle of synchronized is that it uses the flag ACC_SYN-chronized. The executable line first holds the synchronization lock, then executes the method, and finally releases the lock when the method completes.
Synchronized has three main uses:
- Modified instance method: lock the current object instance, before entering the synchronization code to obtain the current object instance lock.
synchronized void method(a) {
// Business code
}
Copy the code
- Modify static methods: That is, lock the current class on all object instances of the class and obtain the lock of the current class before entering the synchronization code. Because A static member does not belong to any instance object, it is A class member (static indicates that it is A static resource of the class, no matter how many objects are new), so if A thread A calls A non-static synchronized method of an instance object, Thread B needs to call the static synchronized method of the class to which the instance object belongs. This is allowed and mutual exclusion will not occur, because the lock occupied by accessing the static synchronized method is the current class lock. The lock used to access non-static synchronized methods is the current instance object lock.
synchronized void staic method(a) {
// Business code
}
Copy the code
- Modifies code block: Specifies the lock object and locks the given object/class. Synchronized (this/object) means acquiring the lock of a given object before entering a synchronized code base. Synchronized (class. Class) means acquiring the lock of the current class before entering the synchronized code
synchronized(this) {
// Business code
}
Copy the code
In summary:
Static methods and synchronized(class) blocks lock classes. The synchronized keyword is added to an instance method to lock the object instance.Copy the code
Synchronized is a weight lock
Synchronized is implemented through a lock inside an object called a monitor. But the essence of the monitor Lock depends on the underlying operating system Mutex Lock to implement. However, the operating system realizes the switch between threads, which requires the conversion from user state to core state. This cost is very high, and the conversion between states takes a relatively long time, which is why Synchronized has low efficiency. Therefore, this type of Lock, which relies on the implementation of the operating system Mutex Lock, is called a “heavyweight Lock.”
Principles and examples of synchronized
In Java, synchronized guarantees that only one thread can execute a method or block of code at any one time (mainly for methods or blocks with shared data). Another important use of synchronized, and indeed important, is that synchronized ensures that changes made by one thread (primarily shared data) are seen by other threads (guaranteed visibility, a complete substitute for Volatile).
Synchronized acts on instance methods
The first example is a simple increment of a class’s shared resource. To ensure multithreading safety, use the synchronized modifier increase(). To increment I, it is not an atomic operation, because the first step is to read I, and the second step is to increment it, so it requires a mutex operation. Synchronized modifies the instance method increase, in which case the lock of the current thread is the instance object. When one thread is accessing the synchronized methods of an object, other threads cannot access all synchronized methods of the object. Because an object has only one lock, when one thread acquires the lock of the object, other threads cannot acquire the lock. Therefore, the synchronized method of the object cannot be accessed, thus protecting the safety of multithreading, but other threads can still access other non-synchronized methods of the object.
public class syntest {
public static void main(String[] args) throws InterruptedException {
AccountingSync instance=new AccountingSync();
Thread t1=new Thread(instance);
Thread t2=newThread(instance); t1.start(); t2.start(); t1.join(); t2.join(); }}class AccountingSync implements Runnable{
static int i=0;
public synchronized void increase(a) throws InterruptedException {
i++;
Thread.sleep(300);
System.out.println(Thread.currentThread().getName()+"Increased the value of I, its value is"+i);
}
@Override
public void run(a) {
for(int j=0; j<100; j++){try {
increase();
} catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code
(omit middle)
Synchronized acts on code blocks
The example cited here is the first insecure example of buying tickets in the last article “Java Multithreading (3) Typical example of multithreading insecurity”. Here is still three people buying tickets, a total of 30 tickets, but the difference is that synchronized is used to lock the code block buying tickets. When writing method body is larger, at the same time, there are some more time-consuming operation, and need to be synchronized code and only a small part, if directly with the method of synchronous operation, may do more harm than good, the way we can use the synchronized code block to package needs to be synchronized code, so you don’t need to the whole method for synchronous operation. Applying synchronized to a given instance object, t, means that the current instance object is the lock object. Each time a thread enters a block of synchronized wrapped code, the current thread is required to hold the lock on the instance object. If another thread is currently holding the lock, the newly arrived thread must wait. Of course, in addition to t as the object, we can also use this object (representing the current instance) or the current class object as the lock.
class Ticket implements Runnable{
private int alltickets = 30;
private boolean flag = true;
@Override
public void run(a) {
while(alltickets>0) {
synchronized (this) {
try {
Thread.sleep(300);
buy();
} catch(InterruptedException e) { e.printStackTrace(); }}}}public void buy(a) throws InterruptedException {
if(this.alltickets<=0)
{
System.out.println("There are no tickets left."+Thread.currentThread().getName());
this.flag = false;
return;
}
else
{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"Bought the number"+this.alltickets--+"Ticket"); }}}public class testThread {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Ticket t = new Ticket();
new Thread(t, "Xiao Hua").start();
new Thread(t, "Xiao Ming").start();
new Thread(t, "Cow").start();
}
Copy the code
It can be seen that the buying process is orderly, and there is no problem of buying duplicate tickets
Synchronized acts on static methods
When synchronized acts on a static method, the lock is the class object lock of the current class. Because static members are not exclusive to any instance object and are class members, concurrent operations on static members can be controlled through class object locks. Note that if thread A calls the non-static synchronized method of an instance object and thread B calls the static synchronized method of the class that the instance object belongs to, the mutual exclusion will not occur. Because a lock used to access a static synchronized method is the current class object, and a lock used to access a non-static synchronized method is the current instance object lock. The synchronized keyword modifies static methods whose lock object is the class object of the current class. Note that the increase2 method in this code is an instance method whose lock is the current instance object. If it is called by another thread, there is no mutual exclusion (lock objects are different, after all), but we should be aware that thread-safety issues can be found in this case (operating on shared static variable I).
public class syntest {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new StaticTest());
Thread t2=new Thread(new StaticTest());
t1.start();t2.start();
}
}
class StaticTest implements Runnable{
static int i=0;
/** * applies to static methods, and the lock is the current class object */ corresponding to the StaticTest class
public static synchronized void increase(a) throws InterruptedException {
i++;
Thread.sleep(300);
System.out.println(Thread.currentThread().getName()+"Increased the value of I, its value is"+i);
}
/** * non-static, access to different locks will not be mutually exclusive */
public synchronized void increase2(a){
i++;
}
@Override
public void run(a) {
for(int j=0; j<100; j++){try {
increase();
} catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code
Synchronized can re-enter the lock
The keyword synchronized has the function of reentrant locking. In synchronized, an object lock can be acquired when a thread acquires it and then requests it again, meaning that in a synchronized method or block of code, This can be done when calling other synchronized methods or blocks of code on this class.
public class syntest {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(newStaticTest()); t1.start(); }}class StaticTest implements Runnable{
public synchronized void method1(a)
{
System.out.println("method1");
method2();
}
public synchronized void method2(a)
{
System.out.println("method2");
method3();
}
public synchronized void method3(a)
{
System.out.println("method3");
}
@Override
public void run(a) { method1(); }}Copy the code