Previous:
Thread join from Java thread to kotlin coroutine
Thread synchronization
In short, when multiple threads want to operate on the same memory address, only one thread can operate on the memory address at the same time, and other threads cannot operate on the memory address. At this time, other threads are in a waiting state, which is thread synchronization. There are many ways to achieve thread synchronization.
Thread synchronization is mainly to solve the problem of data security.
Let's start with a classic example of ticket selling.
For example, a total of 30 tickets are sold in five windows at the same time. The code without synchronization is as follows
object Ticket { /*Total Votes */ var totalCount = 30 /*Number of tickets currently sold*/ private var currentNumber = 1 /*Selling tickets*/ fun sale() { if (totalCount == 0) { println("${Thread.currentThread().name} The tickets are sold out") return } totalCount-- println("${Thread.currentThread().name} Sell No ${currentNumber}Tickets, remaining ${totalCount}Ticket") currentNumber++ TimeUnit.MILLISECONDS.sleep(100) } }
The simulation method of selling 5 tickets at the same time is as follows:
fun main() { for (i in 1..5) { Thread({ while (Ticket.totalCount > 0) { Ticket.sale() } }, "window" + i).start() } }
The operation results are as follows:
It can be seen that the ticket sales data is obviously wrong.
The reason is that multiple threads operate on the count in the Ticket at the same time, resulting in unsafe data and incorrect data accessed by other threads.
Like this, multiple threads operate on a memory address at the same time. In order to ensure data security, we need to do thread synchronization
synchronized keyword
synchronized is a keyword provided by the JVM. This keyword is equivalent to locking resources. To access resources, you must get the lock first. If the lock is held by other threads, the current thread is in the blocked state, waiting for the lock to be released, and then competing for the lock to obtain the execution opportunity.
synchronized can be used in the following code
- Instance method (non static method)
- Static method
- Code block
Let's take a look at the above code with the Java version and the synchronized keyword
public class TestSynchronized { public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { while (Ticket.totalCount > 0) { Ticket.sale(); } }, "window" + i).start(); } } } class Ticket { public static int totalCount = 30; private static int number = 1; /*Selling tickets*/ public static synchronized void sale() { if (totalCount == 0) { System.out.println(Thread.currentThread().getName() + "The tickets are sold out"); return; } totalCount--; System.out.println(Thread.currentThread().getName() + "Sold the second" + number + "Tickets, remaining" + totalCount + "Ticket"); number++; try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
As you can see, we added the synchronized keyword to the sale static method. Let's take a look at the running results
It can be seen that the data is normal.
It should be noted that when using synchronized, you should pay attention to whether the resources of the synchronized lock are the same. The synchronization code will take effect only if the resources of the lock are the same. In short, to achieve synchronization, multiple threads compete for a single lock.
The above is the static method of lock. Now let's try the lock code block
class Ticket { public static int totalCount = 30; private static int number = 1; /*Selling tickets*/ public static void sale() { synchronized (Ticket.class) { if (totalCount == 0) { System.out.println(Thread.currentThread().getName() + "The tickets are sold out"); return; } totalCount--; System.out.println(Thread.currentThread().getName() + "Sold the second" + number + "Tickets, remaining" + totalCount + "Ticket"); number++; } try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
The above synchronization code block locks the ticket Class, we just need to lock the code that needs to be synchronized.
Let's take another look at the running results:
It can be seen that the results are in line with expectations.
Let's try to lock different resources. The code is as follows
class Ticket { public static int totalCount = 30; private static int number = 1; /*Selling tickets*/ public static void sale() { synchronized (new Ticket()) { if (totalCount == 0) { System.out.println(Thread.currentThread().getName() + "The tickets are sold out"); return; } totalCount--; System.out.println(Thread.currentThread().getName() + "Sold the second" + number + "Tickets, remaining" + totalCount + "Ticket"); number++; } try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
In the above code, our synchronization code block lock is to new an object every time. In this way, the lock must not be the same. Let's look at the operation results:
But as soon as I saw it, the data became incorrect again.
The usage of lock instance method is similar, and the code is as follows:
public class TestSynchronized { public static void main(String[] args) { Ticket ticket = new Ticket(); for (int i = 0; i < 5; i++) { new Thread(() -> { while (ticket.totalCount > 0) { ticket.sale(); } }, "window" + i).start(); } } } class Ticket { public int totalCount = 30; private int number = 1; /*Selling tickets*/ public synchronized void sale() { if (totalCount == 0) { System.out.println(Thread.currentThread().getName() + "The tickets are sold out"); return; } totalCount--; System.out.println(Thread.currentThread().getName() + "Sold the second" + number + "Tickets, remaining" + totalCount + "Ticket"); number++; try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
The operation results are as follows:
As you can see, the result is normal.
Let's take a look at how the synchronization methods of different instances work.
The main method code is as follows:
public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { Ticket ticket = new Ticket(); while (ticket.totalCount > 0) { ticket.sale(); } }, "window" + i).start(); } }
The operation results are as follows
This is equivalent to that there are five threads, and each thread performs tasks independently, which has nothing to do with synchronization.
Therefore, when using the synchronized keyword, we should pay special attention to whether the threads compete for the same lock.
Or the above code, we just need to ensure that the instances obtained by different threads are the same.
This is a very common singleton pattern in daily development.
Let's transform the code as follows:
class Ticket { private static Ticket ticket = null; public int totalCount = 30; private int number = 1; private Ticket() { } /*Thread safe singleton mode*/ public static Ticket newInstance() { if (ticket == null) { synchronized (Ticket.class) { if (ticket == null) { synchronized (Ticket.class) { ticket = new Ticket(); } } } } return ticket; } /*Selling tickets*/ public void sale() { synchronized (Ticket.class) { if (totalCount == 0) { System.out.println(Thread.currentThread().getName() + "The tickets are sold out"); return; } totalCount--; System.out.println(Thread.currentThread().getName() + "Sold the second" + number + "Tickets, remaining" + totalCount + "Ticket"); number++; } try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
main method
public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { Ticket ticket = Ticket.newInstance(); System.out.println("ticket Object address" + ticket); while (ticket.totalCount > 0) { ticket.sale(); } }, "window" + i).start(); } }
Let's take another look at the running results:
It can be seen that since we have written the acquisition of Ticket objects into a thread safe singleton mode, there will be no separate execution of five objects.
Well, that's about the synchronized keyword.
If you think this article is helpful to you, please move your finger to help more developers. If there are any mistakes in the article, please correct them. Please indicate the source of reprint Yu Zhiqiang's blog , thank you!