This is the 29th day of my participation in the More Text Challenge. For more details, see more Text Challenge
Related articles
Java Multithreading summary: Java multithreading
preface
The previous article introduced the phenomenon of data errors in the case of concurrency.
- Concurrency: The same object is operated by multiple threads at the same time, that is, different threads at the same resource address, resulting in data clutter.
- Synchronization: Multiple threads that need to access a resource at the same time enter an object’s wait pool and wait until previous threads have finished using it.
- Lock: Each object has a lock. When an object is acquired, it monopolizes the resource and other threads must wait until it is used before releasing it
- One thread holding a lock causes all other threads that need it to hang
- In multithreaded competition, locking and releasing leads to more context switching and scheduling delays, causing performance problems
- If a thread with a higher priority waits for a thread with a lower priority to release the lock, it can cause priority inversion, causing performance problems
One, synchronization method
First, we need to introduce the synchronized keyword.
public synchronized void method(int args){}
Copy the code
-
Synchronized:
- The default lock is on its own object.
- Synchronous blocks are usually used across objects.
- Lock the object where the shared resource resides.
-
Example code:
/** * Mobile phone buying cases */
public class PhoneSnapUp implements Runnable {
private Integer inventory = 10;// Mobile phone inventory
private boolean flag = true;
public static void main(String[] args) {
PhoneSnapUp phoneSnapUp = new PhoneSnapUp();
// Simulate 5 people at the same time, that is, open 5 threads at the same time
new Thread(phoneSnapUp, "Ding Da No.1").start();
new Thread(phoneSnapUp, "Ding Da No. 2").start();
new Thread(phoneSnapUp, "Dingda No. 3").start();
new Thread(phoneSnapUp, "Dingda No.4").start();
new Thread(phoneSnapUp, "Ding Da Wu No. 5").start();
}
@Override
public synchronized void run(a) {
while (flag){
try {
// synchronized (this) {
buy();
/ /}
} catch(Exception e) { e.printStackTrace(); }}}// Give the ticket purchasing method
private void buy(a) throws Exception {
// When the stock is zero, the rush is over
if (inventory <= 0) {
flag = false;
return;
}
// Simulate delay, otherwise the results are not easy to see
Thread.sleep(500);
// For each purchase, the stock goes down by 1
System.out.println("Congratulations!!" + Thread.currentThread().getName() + "-- got a Millet 12! Inventory left:" + --inventory + "Taiwan"); }}Copy the code
- The execution result is as follows:
- Conclusion: The synchronization method can ensure that the results are error-free.
- However, since it is a method, adding synchronized means that everything in the method is locked, which can cause efficiency problems and cause the program to block
- Therefore, we generally use synchronous code block to achieve the lock operation.
2. Synchronize code blocks
synchronized(Obj){}
Copy the code
- Code example: Other code and synchronized method, not repeat paste, here only introduce the run method code.
@Override
public void run(a) {
while (flag){
try {
synchronized (this) { buy(); }}catch(Exception e) { e.printStackTrace(); }}}Copy the code
- Execution Result:
-
Conclusion:
-
The results are consistent with the synchronous method, which proves that both methods can guarantee the accuracy of the results.
-
A synchronized block specifies that the fixed thing is locked, leaving the rest of the method unaffected.
-
To take a rough example, if you go to shit, the synchronous method is equivalent to locking the toilet door directly, and you only use one of the three pits, a typical waste of resources. With synchronous code block, you can lock the door of the pit, and other people can use the other pit, haha image ~
-
-
Note:
- Obj can be any object, but it is recommended to use a shared resource as a synchronization monitor
- There is no need to specify a synchronization monitor in a synchronization method, because the synchronization monitor for a synchronization method is either this, which is the object itself, or a class
-
2. The execution of a synchronization monitor:
- The first thread accesses, locks the synchronization monitor, and executes the code in it
- The second thread accesses and finds that the synchronization monitor is locked and inaccessible
- When the first thread has accessed, unlock the synchronization monitor.
- The second thread accesses, finds that the synchronization monitor has no lock, locks and accesses
Third, a deadlock
With locks mentioned above, a deadlock occurs when two threads have access to a shared resource at the same time and are unable to release it.
-
Simple and crude understanding:
- Two resources, A and B
- Ding yi holds the lock of resource A, and intends to continue to hold the lock of resource B after execution
- D 2 holds the lock on resource B, and then proceeds to hold the lock on resource A
- Both of them did not finish the execution, that is, each other did not release the lock, two people will fight, who can not execute
- Lead to a deadlock
-
The text is always unclear, so follow the code around and you’ll be sure to understand:
/** * Simulate multi-threaded deadlock situation */
public class Demo {
public static void main(String[] args) {
DeadLockThread deadLockThread = new DeadLockThread();
new Thread(deadLockThread,"Ding a +").start();
new Thread(deadLockThread,"T + 2").start(); }}class DeadLockThread implements Runnable {
A a = new A();
B b = new B();
@SneakyThrows
@Override
public void run(a) {
if (Thread.currentThread().getName().equals("Ding")) {synchronized (this){
System.out.println(Thread.currentThread().getName());
a.soutA();
Thread.sleep(2000); b.soutB(); }}else {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
b.soutB();
Thread.sleep(2000); a.soutA(); }}}}class A {
public void soutA(a){
System.out.println("I am A"); }}class B {
public void soutB(a){
System.out.println("I am B"); }}Copy the code
- The execution result is as follows:
-
Summary: The four conditions necessary to generate a deadlock:
- Mutually exclusive: A resource can only be used by one process at a time
- Request and hold condition: when a process is blocked by a request for a resource, it holds on to a resource it has acquired
- Non-deprivation condition: A process cannot forcibly take away resources it has obtained until it has used them up
- Cyclic wait condition: Several processes form an end-to-end cyclic wait resource relationship
-
A deadlock can be avoided simply by breaking either one
Fourth, the Lock locks
-
Synchronization is achieved by displaying the defined synchronization Lock object (Lock)
-
The ReentrantLock class implements the Lock interface, which has the same concurrency and memory semantics as synchronized. The more common approach to achieving thread-safe control is ReentrantLock, which can explicitly Lock and release locks.
-
Code examples:
/** * Use Lock */
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock = new TestLock2();
new Thread(testLock, "A").start();
new Thread(testLock, "B").start();
new Thread(testLock, "C").start(); }}class TestLock2 implements Runnable{
int count = 1000;
// Define lock
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run(a) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(true)
{
try {
// Enter the lock state
lock.lock();
if(count > 0)
System.out.println(Thread.currentThread().getName() + "-" +count--);
else
break;
}
finally {
/ / unlocklock.unlock(); }}}}Copy the code
- Execution Result:
- Conclusion:
- Without a lock, ABC can operate on count at the same time, resulting in data clutter
- After the lock is added, ABC queues for operation
I see no ending, but I will search high and low
If you think I blogger writes good! Writing is not easy, please like, follow, comment to encourage the blogger ~hahah