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