Synchronized to introduce
Java thread is mapped to the operating system’s native threads, if you want to block or wake up a single thread, all need to done with the help of the operating system, which requires from user mode to kernel mode, the state transition requires a lot of CPU time, therefore, for the simple synchronized block, state transition time may be longer than the user code, Synchronized is therefore a heavyweight operation in the Java language.
The synchronized keyword is compiled and forms two bytecode instructions, monitorenter and Monitorenxit, respectively, before and after the synchronized block. Both of these bytecode instructions require a reference parameter to specify the unlocked object to be locked.
As per the vm specification, when monitorenter executes, it first attempts to acquire the lock on an object. If the object is not locked or the current thread already owns the lock, it increments the lock counter by one. Accordingly, monitorexit decrement the lock counter by one. The lock is released. If the object lock fails to be acquired, the current thread blocks and waits until the object lock is released by the owning thread
Synchronized blocks are reentrant to the same thread and do not lock themselves.
Volatile guarantees order and visibility, but does not guarantee atomicity. Synchronized guarantees atomicity, order and visibility.
Why synchronized
Let’s look at a case of selling tickets
@RunWith(SpringRunner.class)
@SpringBootTest
public class SynchronizedTest {
@Autowired
private ExecutorService newFixThreadPool;
// One hundred tickets
private int ticket = 100;
public void increase(a) {
// simulate ticketing
if (ticket == 0) {
System.out.println("Sold out.");
} else {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println("Current remaining votes"+ ticket); }}// Use a thread pool to perform ticketing
@Test
public void addTest(a) {
for (int i = 1; i <= 200; i++) {
Runnable task = new Runnable() {
@Override
public void run(a) { increase(); }}; newFixThreadPool.execute(task); }}}Copy the code
The above code will cause a problem, that is, the oversold problem, let’s analyze the reason:
Under concurrent conditions, for example, when there is only one ticket left, multiple threads will judge whether there is still a ticket, and the judgment will be greater than 0, so they will enter the business logic of ticket sales, so the phenomenon of overselling will occur. Therefore, we can introduce the heavyweight synchronized lock to control the logic that only one thread can enter the ticket at the same time, so that there is no oversold phenomenon.
Three applications of synchronized
Common synchronization method
In normal synchronization methods (instance methods), the lock is the current instance object, and the lock of the current instance is acquired before entering the synchronization code
@RunWith(SpringRunner.class)
@SpringBootTest
public class SynchronizedTest {
@Autowired
private ExecutorService newFixThreadPool;
// One hundred tickets
private int ticket = 100;
// apply to synchronous methods
public synchronized void increase(a) {
// simulate ticketing
if (ticket == 0) {
System.out.println("Sold out.");
} else {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println("Current remaining votes"+ ticket); }}// Use a thread pool to perform ticketing
@Test
public void addTest(a) {
for (int i = 1; i <= 200; i++) {
Runnable task = new Runnable() {
@Override
public void run(a) { increase(); }}; newFixThreadPool.execute(task); }}}Copy the code
Running results:
As you can see, the oversold problem has been solved.
Statically synchronized method
Static synchronization method, the lock is the class of the current class, before entering the synchronization code to obtain the lock of the current class object.
// apply to static methods
public static synchronized void increase(a) {
// simulate ticketing
if (ticket == 0) {
System.out.println("Sold out.");
} else {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println("Current remaining votes"+ ticket); }}Copy the code
Synchronized code block
Synchronized code block, lock is the object inside parentheses, to the given object lock, enter the synchronized code block to obtain the lock of the given object.
// Applies to synchronized code blocks
public void increase(a) {
synchronized (this) {
// simulate ticketing
if (ticket == 0) {
System.out.println("Sold out.");
} else {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println("Current remaining votes"+ ticket); }}}Copy the code
In fact, we use the usual locks, and the JVM automatically optimizes them. These include biased locks, lightweight locks, spin locks and adaptive spin locks, lock elimination, and lock coarser. For more information, see thread safety and lock optimization in my previous thread safety post
>> The volatile keyword
Thread pools are useful in this article. If you need to know more about them, you can read :>> Thoroughly understand thread pools
Thank you for your ❤️ attention + thumbs up ❤️, original is not easy, encourage the author to create better articles