ReentrantLock in JUC from Java thread to kotlin coroutine

Previous:

From Java thread to kotlin coroutine util. concurrent. Interfaces and implementation classes in locks package

ReentrantLock

The re-entry Lock can be locked many times, and the Lock interface is implemented
Unlike synchronized, synchronized locks and unlocks automatically, while the implementation class of Lock interface needs to Lock and unlock manually.

Let's take a look at the methods in ReentrantLock. The commonly used methods are actually the methods to implement the Lock interface.

Let's use it briefly first, or the example of buying a ticket before:

lock()

Obtain the lock. If the lock is held by another thread, it will be in a blocking state and wait until the lock is obtained

class Ticket {
    public static int totalCount = 30;
    private static int number = 1;
    /*Reentry lock*/
    private final static ReentrantLock reentrantLock = new ReentrantLock();

    /*Selling tickets*/
    public static void sale() {

        /*Acquire lock*/
        reentrantLock.lock();

        try {
            if (totalCount == 0) {
                System.out.println("The tickets are sold out");
                return;
            }
            totalCount--;
            System.out.println(Thread.currentThread().getName() + "Sold the second" + number + "Tickets, remaining" + totalCount + "Ticket");
            number++;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /*Release lock*/
            reentrantLock.unlock();
        }
    }
}

The main method is as follows:

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                while (Ticket.totalCount > 0) {
                    Ticket.sale();
                }
            }, "window" + i).start();
        }
    }

See the operation results:

It can be seen that the data is ok, but only one thread is working, and other threads have not obtained the lock. When we used synchronized, we used sleep before locking to enable other threads to obtain the lock and get execution opportunities.
However, if you use ReentrantLock, you don't need to write it like this. Let's take a look at the construction method of ReentrantLock:

Two constructs, one of which accepts a Boolean value. If true is passed in, it means that the current lock is a fair lock, and false is an unfair lock

Fair lock:
Obtaining locks is obtained in the order of locking. Queuing is always the first place in the queue to obtain locks.

Unfair lock:
Obtaining a lock is a preemptive mechanism. One thread may obtain a lock all the time, and other threads may not obtain a lock all the time. The default is a non fair lock

Let's pass in true to the construction of the above code and take another look:

The operation results are as follows:

As you can see, each thread gets an execution opportunity.

tryLock()

Try to obtain the lock immediately. If it arrives, return true; otherwise, return false. The difference from the lock method is that it won't wait all the time. You can do other operations if you can't get the lock.

For example, go to Tony's shop to cut your hair. If you can do it without waiting in line, you can do it directly. If you go and find Tony is cutting someone else, you can't do it and cut it next time.

Easy to use:

class Tony {

    private final ReentrantLock reentrantLock = new ReentrantLock();

    /*haircut*/
    public void cutHair() {

        String name = Thread.currentThread().getName();
        System.out.println(name + "Go into the shop and cut your hair");
        if (reentrantLock.tryLock()) {
            try {
                System.out.println(name + "In line,Start cutting your hair...");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(name + "The hair has been cut,Beautiful...");

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                /*Release lock*/
                reentrantLock.unlock();
            }

        } else {
            /*Lock not acquired*/
            System.out.println(name + "notice tony The teacher is cutting other people's hair. It's very uncomfortable. I won't wait. I'll cut it next time");
        }
    }
}

main method:

    public static void main(String[] args) {

        Tony tony = new Tony();

        new Thread(tony::cutHair, "yzq").start();
        new Thread(tony::cutHair, "xeon").start();

    }

The operation results are as follows:

tryLock(long time, TimeUnit unit)

It is similar to tryLock, except that there are multiple timeout times for obtaining the lock. If the lock is obtained within the specified time period, it returns true, otherwise it returns false.
In the above example, go to teacher Tony's shop to cut your hair. If you can do it without waiting in line, you can do it directly. If you go and find Tony cutting for others, you can wait 2 seconds. If you haven't finished cutting in 2 seconds, you can't wait. You can cut it next time.

class Tony {

    private final ReentrantLock reentrantLock = new ReentrantLock();

    /*haircut*/
    public void cutHair() {

        String name = Thread.currentThread().getName();
        System.out.println(name + "Go into the shop and cut your hair");
        try {
            if (reentrantLock.tryLock(2, TimeUnit.SECONDS)) {
                System.out.println(name + "In line,Start cutting your hair...");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(name + "The hair has been cut,Beautiful...");

            } else {
                /*Lock not acquired*/
                System.out.println(name + "notice tony The teacher is cutting someone's hair. If he hasn't finished cutting it for 2 seconds, he won't wait. Cut it next time");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            /*Determine whether the current thread holds the lock. If yes, release it. Otherwise, it will not be processed*/
            if (reentrantLock.isHeldByCurrentThread()) {
                reentrantLock.unlock();
            }

        }
    }
}

The only thing to note is that if you release the lock in finally, you need to judge whether the current thread holds the lock: isHeldByCurrentThread. If yes, you can release the lock, otherwise it will be reported
IllegalMonitorStateException.

Operation results:

Well, that's what ReentrantLock usually does.

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!

Keywords: JUC ReentrantLock

Added by H4mm3r3r on Sat, 29 Jan 2022 15:11:47 +0200