The lock

A, the Lock Lock

Lock is an interface that has three implementation classes, of which reentrant locking is the most common.

  • Fair lock: multiple threads acquire locks in the same order as they apply for locks, and the thread directly enters the queue to queue. The thread is always the first in the queue to obtain locks.
    • Advantages: All threads get resources and do not starve to death in the queue.
    • Disadvantages: Throughput drops dramatically, all but the first thread in the queue blocks, and it is expensive for the CPU to wake up blocked threads.
public class Lock1 {
    public static void main(String[] args) {
        Ticket1 ticket = new Ticket1();
        new Thread(()->{
            for(int i=0; i<40; i++) ticket.sale(); }).start();new Thread(()->{
            for(int i=0; i<40; i++) ticket.sale(); }).start();new Thread(()->{
            for(int i=0; i<40; i++) ticket.sale(); }).start(); }static class Ticket1{
        private int ticket = 30;
        Lock lock = new ReentrantLock(false);
        // Business logic
        private void sale(a){
            / / lock
            lock.lock();
            try {
                if(ticket>0)
                    System.out.println(Thread.currentThread().getName()+"Bought a ticket,"+"Sold the first."+ (30-(--ticket))+"One ticket left."+ticket+"Ticket");
            }catch (Exception e){

            }finally {
                // Will the lock be released anywaylock.unlock(); }}}}Copy the code
So Thread-0 buys one ticket, sells the first ticket, has 29 tickets left, thread-0 buys one ticket, sells the second ticket, has 28 tickets left, thread-1 buys one ticket, sells the third ticket, has 27 tickets left, thread-2 buys one ticket, sells the fourth ticket, There are 26 tickets left and Thread-0 buys one ticket, sells the fifth ticket, has 25 tickets left and Thread-1 buys one ticket, sells the sixth ticket, has 24 tickets left and Thread-2 buys one ticket, sells the seventh ticket, has 23 tickets left and thread-0 sells the eighth ticket, There are 22 tickets left and Thread-1 buys one ticket, sells the ninth ticket, has 21 tickets left and Thread-2 buys one ticket, sells the tenth ticket, has 20 tickets left and Thread-0 buys one ticket, sells the 11th ticket, There are 19 tickets left and Thread-1 buys one ticket, sells the 12th ticket, there are 18 tickets left and Thread-2 buys one ticket, sells the 13th ticket, there are 17 tickets left and Thread-0 buys one ticket, sells the 14th ticket, There are 16 tickets left and Thread-1 buys one ticket, sells the 15th ticket, there are 15 tickets left and Thread-2 buys one ticket, sells the 16th ticket, there are 14 tickets left and Thread-0 buys one ticket, sells the 17th ticket, There are 13 tickets left and Thread-1 buys one ticket, sells the 18th ticket, there are 12 tickets left and Thread-2 buys one ticket, sells the 19th ticket, there are 11 tickets left and Thread-0 buys one ticket, sells the 20th ticket, There are 10 tickets left and Thread-1 buys one ticket and sells the 21st ticket, there are 9 tickets left and Thread-2 buys one ticket and sells the 22nd ticket, there are 8 tickets left and Thread-0 buys one ticket and sells the 23rd ticket, So there are seven tickets left and Thread-1 buys one ticket, sells the 24th ticket, there are six tickets left and Thread-2 buys one ticket, sells the 25th ticket, there are five tickets left and Thread-0 buys one ticket, sells the 26th ticket, there are four tickets left and Thread-1 sells the 27th ticket, There are 3 tickets left and Thread-2 has purchased one ticket, sold the 28th ticket, and there are 2 tickets left and Thread-0 has purchased one ticket, sold the 29th ticket, and has sold the 30th ticket, so there are 0 tickets leftCopy the code
  • Unfair lock: When multiple threads try to obtain the lock, they will directly try to obtain it. If they fail to obtain the lock, they will enter the waiting queue. If they can obtain the lock, they will directly obtain the lock.
    • Advantages: can reduce the CPU wake up thread overhead, the overall throughput efficiency will be higher, CPU does not have to wake up all threads, will reduce the number of wake up threads.
    • Disadvantages: As you may have noticed, this can cause a thread in the middle of the queue to remain without a lock or for a long time, leading to starvation.
public class Lock1 {
    public static void main(String[] args) {
        Ticket1 ticket = new Ticket1();
        new Thread(()->{
            for(int i=0; i<40; i++) ticket.sale(); }).start();new Thread(()->{
            for(int i=0; i<40; i++) ticket.sale(); }).start();new Thread(()->{
            for(int i=0; i<40; i++) ticket.sale(); }).start(); }static class Ticket1{
        private int ticket = 30;
        Lock lock = new ReentrantLock();
        // Business logic
        private void sale(a){
            / / lock
            lock.lock();
            try {
                if(ticket>0)
                    System.out.println(Thread.currentThread().getName()+"Bought a ticket,"+"Sold the first."+ (30-(--ticket))+"One ticket left."+ticket+"Ticket");
            }catch (Exception e){

            }finally {
                // Will the lock be released anywaylock.unlock(); }}}}Copy the code
Thread0 bought one ticket, sold the first one, sold twenty-nine tickets left, bought one ticket, sold the second one, sold twenty-eight tickets left, bought one ticket, sold the third one, sold the fourth one, There are 26 tickets left and Thread-0 buys one ticket, sells the fifth ticket, there are 25 tickets left and thread-0 buys one ticket, sells the sixth ticket, there are 24 tickets left and thread-0 buys one ticket, sells the seventh ticket, there are 23 tickets left and thread-0 sells the eighth ticket, There are 22 tickets left and Thread-0 buys one ticket, sells the ninth ticket, has 21 tickets left and thread-0 buys one ticket, sells the tenth ticket, has 20 tickets left and thread-0 buys one ticket, sells the 11th ticket, There are 19 tickets left and Thread-0 buys one ticket, sells the 12th ticket, there are 18 tickets left and thread-0 buys one ticket, sells the 13th ticket, there are 17 tickets left and thread-0 buys one ticket, sells the 14th ticket, There are 16 tickets left and Thread-0 buys one ticket, sells the 15th one, has 15 tickets left, thread-0 buys one ticket, sells the 16th one, has 14 tickets left and sells the 17th one, With 13 tickets left, Thread-0 bought one ticket, sold the 18th ticket, had 12 tickets left, thread-0 bought one ticket, sold the 19th ticket, had 11 tickets left, thread-0 bought one ticket, sold the 20th ticket, There are 10 tickets left and Thread-0 buys one ticket and sells the 21st ticket, there are 9 tickets left and thread-0 buys one ticket and sells the 22nd ticket, there are 8 tickets left and thread-0 buys one ticket and sells the 23rd ticket, There are seven tickets left and Thread-0 buys one ticket, sells the 24th ticket, there are six tickets left and thread-0 buys one ticket, sells the 25th ticket, there are five tickets left and thread-0 buys one ticket, sells the 26th ticket, there are four tickets left and thread-0 sells the 27th ticket, There are three tickets left and Thread-0 buys one ticket, sells the 28th ticket, and there are two tickets left. Thread-0 buys one ticket, sells the 29th ticket, and then sells the 30th ticket, and there are zero tickets leftCopy the code

1.1 Differences between synchronized and Lock

  • Synchronized is a built-in Java keyword and Lock is a Java class
  • Synchronized cannot determine the status of a Lock. Lock can determine whether a Lock is obtained
  • Synchronized automatically releases the lock. You must manually release the lock. If the lock is not released, a deadlock occurs
  • Two threads use synchronized. Thread 1 acquires the lock and blocks, while thread 2 waits. Lock The Lock doesn’t have to wait forever
  • Synchronized can reentrant lock, can not interrupt, unfair; Lock, reentrant Lock, interruptible, unfair (can be set directly)
  • Synchronized is good for locking small amounts of code, and Lock is good for locking large amounts of code

1.2 Using synchronized, how to determine who is locked

  • New this, generates an object, locks the specific object
  • Static: locks a Class, a unique template, no matter how many objects are generated

A synchronized modified method whose object is the caller of the method, that is, the new phone object

The two methods are the same lock, the first to get the first to execute, the other thread blocks

1In standard case, which of the two lines should be printed first, the result should be printed first, and the message should be sent2, send SMS delay 2s, which two lines print first, the result is print first, send SMSpublic class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        newThread(()->{ phone.call(); }).start(); }}class Phone{
    public synchronized  void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public synchronized void call(a){
        System.out.println("Make a call"); }}Copy the code

After there are two objects, there are two locks, the SMS line is executed first, but there is a delay, and then the phone call is not the same lock as it, and it is printed when it is executed

3, add a common method, first execute SMS or Hello, the result is Hello4, two objects, two synchronization methods, text first or call, the result is callpublic class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Phone2 phone = new Phone2();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.hello();
        }).start();
    }
	public static void main(String[] args) throws InterruptedException {
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        newThread(()->{ phone2.call(); }).start(); }}class Phone2{
    public synchronized  void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public synchronized void call(a){
        System.out.println("Make a call");
    }

    public void hello(a){
        System.out.println("hello"); }}Copy the code

The Class template of a statically synchronized method lock has only one template, that is, the same lock, no matter how many new objects are created

5Add a static synchronization method that results in SMS first6And in the5On the basis ofnewAn object phone2 that calls phone2 will result in SMS firstpublic class Test3 {
    public static void main(String[] args) throws InterruptedException {
        Phone3 phone = new Phone3();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.call();
        }).start();
        Phone3 phone2 = new Phone3();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        newThread(()->{ phone2.call(); }).start(); }}class Phone3{
    // Static methods are available as soon as the Class is loaded, with only one Class template
    // Synchronize the Class of the static method lock
    public static synchronized  void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public static synchronized void call(a){
        System.out.println("Make a call"); }}Copy the code

For two objects, the static method and the normal synchronous method do not have the same lock, so it does not matter.

7, a static synchronization method, a normal synchronization method, an object, the result is call first8, a static synchronization method, a normal synchronization method, two objects, the result is call firstpublic class Test4 {
    public static void main(String[] args) throws InterruptedException {
        Phone4 phone = new Phone4();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.call();
        }).start();
        Phone4 phone2 = new Phone4();
        new Thread(()->{
            phone.sendSms();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        newThread(()->{ phone2.call(); }).start(); }}class Phone4{
    // Static methods are available as soon as the Class is loaded, with only one Class template
    // Synchronize the Class of the static method lock
    public static synchronized  void sendSms(a){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Texting");
    }
    public synchronized void call(a){
        System.out.println("Make a call"); }}Copy the code

1.3 Reentrant lock

A reentrant lock is a lock that is repeatable and recursively invocable. A lock that is used on an outer layer can still be used on an inner layer without deadlock (provided it is the same object or class). ReentrantLock and synchronized are both reentrantlocks. Here is an example of synchronized:

public class Test {

    public synchronized void get(a){
        System.out.println(Thread.currentThread().getName()+"get");
        set();
    }

    public synchronized void set(a){
        System.out.println(Thread.currentThread().getName()+"set");
    }


    public static void main(String[] args) {
        Test test = new Test();
        for (int i = 0; i < 10; i++) {
        newThread(()->{ test.get(); }).start(); }}}Copy the code

As a result, there is no deadlock in the process

Thread-0get
Thread-0set
Thread-7get
Thread-7set
Thread-6get
Thread-6set
Thread-5get
Thread-5set
Thread-4get
Thread-4set
Thread-3get
Thread-3set
Thread-1get
Thread-1set
Thread-2get
Thread-2set
Thread-9get
Thread-9set
Thread-8get
Thread-8set
Copy the code

Set () and get() both output thread names, indicating that no deadlocks occur even when synchronized is used recursively, proving that it is reentrant.

1.4 No reentrant lock

A non-reentrant lock, as opposed to a reentrant lock, does not recursively call, and a recursion call deadlocks. Next time try designing a non-reentrant lock yourself.

public class UnreentrantLock {
    private boolean isLocked = false;
    public synchronized void lock(a) throws InterruptedException{
        while(isLocked){
            wait();
        }
        isLocked = true;
    }
    public synchronized void unlock(a){
        isLocked = false; notify(); }}Copy the code

Use the lock

public class UnreentrantLockDemo {
    private UnreentrantLock lock = new UnreentrantLock();
    public void get(a) throws InterruptedException {
        lock.lock();
        System.out.println(Thread.currentThread().getName()+" get");
        set();
        lock.unlock();
    }

    public void set(a) throws InterruptedException {
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"set");
        lock.unlock();
    }

    public static void main(String[] args) throws InterruptedException {
        UnreentrantLockDemo demo = newUnreentrantLockDemo(); demo.get(); }}Copy the code

The current thread executes the get() method first to acquire the lock, and then executes the set() method. After entering the set() method, the lock cannot be blocked and must be acquired first. This example proves that the lock is not reentrant.

ReadWriteLock Read/write lock

Shared lock (read lock) Exclusive lock (write lock)public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 1; i <=20 ; i++) {
            final int temp = i;
            if(i%2! =0) {new Thread(()->{
                    myCache.put(temp+"",temp+"");
                }).start();
            }else {
                new Thread(()->{
                    myCache.get(temp-1+""); }).start(); }}}}/** * Custom cache */
 class MyCache{
     private volatile Map<String,Object> map = new HashMap<>();
     private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
     // Write, only one thread at a time
     public void put(String key,Object value){
         readWriteLock.writeLock().lock();
         try {
             System.out.println(Thread.currentThread().getName()+"Written"+key);
             map.put(key,value);
             System.out.println(Thread.currentThread().getName()+"Write" Ok "to");
         } catch (Exception e) {
             e.printStackTrace();
         } finally{ readWriteLock.writeLock().unlock(); }}public void get(String key){
         readWriteLock.readLock().lock();
         try {
             System.out.println(Thread.currentThread().getName()+"Read"+key);
             System.out.println(Thread.currentThread().getName()+"Read data"+map.get(key));
         } catch (Exception e) {
             e.printStackTrace();
         } finally{ readWriteLock.readLock().unlock(); }}}Copy the code

Results:

Thread-0 writes 1 thread-0 writes Ok thread-2 writes 3 Thread-2 writes Ok thread-3 Reads 3 Thread-3 reads data 3 Thread-5 reads data 5 Thread-1 reads 1 thread-5 reads data NULL Thread-1 reads data. 1 Thread-10 writes data. 11 Thread-10 writes DATA to Ok thread-4 writes data to 5 thread-4 writes data to Ok thread-12 Writes data to Ok thread-6 writes data to 7 thread-6 writes DATA to Ok Thread-7 Reads data 7 Thread-7 Reads data 7 Thread-16 writes data 17 Thread-16 writes Ok thread-8 writes data 9 Thread-8 writes Ok thread-19 Reads data 19 Thread-13 reads data Thread-11 Reads the data. 11 Thread-9 Reads the data. 9 Thread-11 Reads the data. 11 Thread-13 Reads the data Thread-14 Writes Ok thread-15 reads data 15 Thread-17 Reads data 17 Thread-15 Reads data 15 Thread-17 Reads data 17 Thread-18 writes DATA 19 Thread-18 writes OkCopy the code

PS: In fact, multiple threads can read at the same time without locking, but there may be threads writing when reading, resulting in illusory read. After adding a read lock, the operation of a read cannot be written before the completion