Copy the code code as follows:
public synchronized void run()
{
}
As can be seen from the above code, as long as the synchronized keyword is added between void and public, the run method can be synchronized. That is to say, for the object instance of the same Java class, the run method can only be called by one thread at the same time. , and can be called by other threads only after the current run is executed. Even if the current thread executes the yield method in the run method, it only pauses for a while. Since other threads cannot execute the run method, the current thread will eventually continue execution. Take a look at the following code first:
The sychronized keyword is only bound to one object instance
Copy the code code as follows:
class Test
{
public synchronized void method()
{
}
}
public class Sync implements Runnable
{
private Test test;
public void run()
{
test.method();
}
public Sync(Test test)
{
this.test = test;
}
public static void main(String[] args) throws Exception
{
Test test1 = new Test();
Test test2 = new Test();
Sync sync1 = new Sync(test1);
Sync sync2 = new Sync(test2);
new Thread(sync1).start();
new Thread(sync2).start();
}
}
The method methods in the Test class are synchronous. But the above code creates two instances of the Test class, so the method methods of test1 and test2 are executed separately. To synchronize the methods, you must pass an instance of the same Test class into its constructor when creating an instance of the Sync class, as shown in the following code:
Sync sync1 = new Sync(test1);
Not only can you use synchronized to synchronize non-static methods, you can also use synchronized to synchronize static methods. For example, the method method can be defined as follows:
Copy the code code as follows:
class Test
{
public static synchronized void method() { }
}
Create an object instance of the Test class as follows:
Test test = new Test();
For static methods, as long as the synchronized keyword is added, the method is synchronized. Whether you use test.method() or Test.method() to call the method method, the method is synchronized and does not exist. Problem with multiple instances of non-static methods.
Among the 23 design patterns, the Singleton mode is also thread-unsafe if designed according to traditional methods. The following code is a thread-unsafe Singleton mode.
Copy the code code as follows:
package test;
// Thread-safe Singleton mode
class Singleton
{
private static Singleton sample;
private Singleton()
{
}
public static Singleton getInstance()
{
if (sample == null)
{
Thread.yield(); // In order to amplify the thread insecurity of Singleton mode
sample = new Singleton();
}
return sample;
}
}
public class MyThread extends Thread
{
public void run()
{
Singleton singleton = Singleton.getInstance();
System.out.println(singleton.hashCode());
}
public static void main(String[] args)
{
Thread threads[] = new Thread[5];
for (int i = 0; i < threads.length; i++)
threads[i] = new MyThread();
for (int i = 0; i < threads.length; i++)
threads[i].start();
}
}
The yield method is called in the above code to make the thread unsafety of singleton mode appear. If this line is removed, the above implementation is still thread unsafe, but the possibility of occurrence is much smaller.
The results of running the program are as follows:
Copy the code code as follows:
25358555
26399554
7051261
29855319
5383406
The above running results may be different in different running environments, but generally the five lines of output will not be exactly the same. As can be seen from this output, there are five object instances obtained through the getInstance method, not one as we expected. This is because when a thread executes Thread.yield(), it hands the CPU resources to another thread. Since the statement to create a Singleton object instance is not executed when switching between threads, these threads all pass the if judgment, so five object instances will be created (four may be created). Or three object instances, depending on how many threads pass the if judgment before creating the Singleton object, the result may be different each time it is run).
To make the above singleton mode thread-safe, just add the synchronized keyword to getInstance. The code is as follows:
public static synchronized Singleton getInstance() { }
Of course, there is a simpler way, which is to create a Singleton object when defining the Singleton variable. The code is as follows:
private static final Singleton sample = new Singleton();
Then just return the sample directly in the getInstance method. Although this method is simple, it is not flexible in creating a Singleton object in the getInstance method. Readers can choose to use different methods to implement the singleton pattern according to specific needs.
There are four points to note when using the synchronized keyword:
1. The synchronized keyword cannot be inherited.
Although you can use synchronized to define methods, synchronized is not part of the method definition, so the synchronized keyword cannot be inherited. If a method in the parent class uses the synchronized keyword and overrides this method in the subclass, the method in the subclass is not synchronized by default and must be explicitly specified in the subclass. Just add the synchronized keyword to the method. Of course, you can also call the corresponding method in the parent class in the subclass method. In this way, although the method in the subclass is not synchronous, the subclass calls the synchronization method of the parent class. Therefore, the method of the subclass is equivalent to synchronization. . Example codes for these two methods are as follows:
Add the synchronized keyword to the subclass method
Copy the code code as follows:
class Parent
{
public synchronized void method() { }
}
class Child extends Parent
{
public synchronized void method() { }
}
Call the synchronized method of the parent class in the subclass method
Copy the code code as follows:
class Parent
{
public synchronized void method() { }
}
class Child extends Parent
{
public void method() { super.method(); }
}
2. The synchronized keyword cannot be used when defining interface methods.
3. The constructor cannot use the synchronized keyword, but can use the synchronized block to be discussed in the next section for synchronization.
4. synchronized can be placed freely.
In the previous examples, the synchronized keyword is placed in front of the return type of the method. But this isn't the only place synchronized can be placed. In non-static methods, synchronized can also be placed at the front of the method definition. In static methods, synchronized can be placed in front of static. The code is as follows:
Copy the code code as follows:
public synchronized void method();
synchronized public void method();
public static synchronized void method();
public synchronized static void method();
synchronized public static void method();
But please note that synchronized cannot be placed after the method return type. For example, the following code is wrong:
Copy the code code as follows:
public void synchronized method();
public static void synchronized method();
The synchronized keyword can only be used to synchronize methods, not class variables. The following code is also wrong.
Copy the code code as follows:
public synchronized int n = 0;
public static synchronized int n = 0;
Although using the synchronized keyword synchronization method is the safest synchronization method, extensive use of the synchronized keyword will cause unnecessary resource consumption and performance loss. Although on the surface it seems that synchronized locks a method, in fact synchronized locks a class. In other words, if synchronized is used when defining both non-static methods method1 and method2, method2 cannot be executed before method1 is executed. The situation is similar for static and non-static methods. But static and non-static methods do not affect each other. Take a look at the following code:
Copy the code code as follows:
package test;
public class MyThread1 extends Thread
{
public String methodName;
public static void method(String s)
{
System.out.println(s);
while(true)
}
public synchronized void method1()
{
method("non-static method1 method");
}
public synchronized void method2()
{
method("non-static method2 method");
}
public static synchronized void method3()
{
method("static method3 method");
}
public static synchronized void method4()
{
method("static method4 method");
}
public void run()
{
try
{
getClass().getMethod(methodName).invoke(this);
}
catch (Exception e)
{
}
}
public static void main(String[] args) throws Exception
{
MyThread1 myThread1 = new MyThread1();
for (int i = 1; i <= 4; i++)
{
myThread1.methodName = "method" + String.valueOf(i);
new Thread(myThread1).start();
sleep(100);
}
}
}
The running results are as follows:
Copy the code code as follows:
Non-static method1 method
Static method3 method
As can be seen from the above running results, method2 and method4 cannot run before method1 and method3 are completed. Therefore, we can draw a conclusion that if you use the synchronized keyword to define a non-static method in a class, it will affect all non-static methods defined using the synchronized keyword in this class. If a static method is defined, it will affect all static methods defined using the synchronized keyword in the class. This is a bit like a table lock in a data table. When a record is modified, the system locks the entire table. Therefore, extensive use of this synchronization method will significantly reduce the performance of the program.