Various locks in Java

reentrant lock

Recursive lock, the same thread, the outer function acquires the lock, and the inner one also acquires the lock.

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

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

lock and unlock must appear in pairs, if they do not appear in pairs, the program cannot end

  private void get() {
    
    
    try {
    
    
      lock.lock();
      System.out.println(Thread.currentThread().getName() + " get.....");
      set();
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      lock.unlock();
    }
  }

  private void set() throws Exception {
    
    
    try {
    
    
      lock.lock();
      System.out.println(Thread.currentThread().getName() + " set.....");
    } finally {
    
    
      lock.unlock();
    }

spin lock

The thread that acquires the lock will not block immediately, but acquires the lock in a circular manner, which reduces the switching of thread context and consumes CPU resources in a circular manner

public class SpinLockDemo {
    
    

  AtomicReference<Thread> atomicReference = new AtomicReference<>();

  public void myLock() {
    
    
    Thread thread = Thread.currentThread();
    System.out.println(thread.getName() + " come in.......");
    while (!atomicReference.compareAndSet(null, thread)) {
    
    

    }
  }

  public void myUnLock() {
    
    
    Thread thread = Thread.currentThread();
    atomicReference.compareAndSet(thread, null);
    System.out.println(Thread.currentThread().getName() + " myUnlock()....");
  }


  public static void main(String[] args) {
    
    
    SpinLockDemo lockDemo = new SpinLockDemo();

    new Thread(() -> {
    
    
      lockDemo.myLock();
      try {
    
    
        TimeUnit.SECONDS.sleep(5);
      } catch (InterruptedException e) {
    
    
        e.printStackTrace();
      }
      lockDemo.myUnLock();
    }, "AA").start();

    try {
    
    
      TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    
    
      e.printStackTrace();
    }
    new Thread(() -> {
    
    
      lockDemo.myLock();
      lockDemo.myUnLock();
    }, "BB").start();

  }

}

shared and exclusive locks

class MyCache {
    
    
  // 保证可见性
  private volatile Map<String, Object> map = new HashMap<>();
  // 保证了原子性,但是不能读
  // private Lock lock = new ReentrantLock();
  private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

  public void put(String key, Object value) {
    
    
    readWriteLock.writeLock().lock();
    try {
    
    
      System.out.println(Thread.currentThread().getName() + "   写入....");
      TimeUnit.SECONDS.sleep(1);
      map.put(key, value);
      System.out.println(Thread.currentThread().getName() + " finished 写入....");
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      readWriteLock.writeLock().unlock();
    }
  }

  public Object get(String key) {
    
    
    readWriteLock.readLock().lock();
    try {
    
    
      System.out.println(Thread.currentThread().getName() + " 读取.....");
      TimeUnit.SECONDS.sleep(1);
      Object o = map.get(key);
      System.out.println(Thread.currentThread().getName() + " finished 读取.....");
      return o;
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      readWriteLock.readLock().unlock();
    }
    return null;
  }

}


public class ReadWriteDemo {
    
    

  public static void main(String[] args) {
    
    
    MyCache myCache = new MyCache();

    for (int i = 0; i < 5; i++) {
    
    
      final int tempInt = i;
      new Thread(() -> {
    
    
        myCache.put(tempInt + "", tempInt + "");
      }, String.valueOf(i)).start();
    }

    for (int i = 0; i < 5; i++) {
    
    
      final int tempInt = i;
      new Thread(() -> {
    
    
        myCache.get(tempInt + "");
      }, String.valueOf(i)).start();
    }
  }

}
0   写入....
0 finished 写入....
1   写入....
1 finished 写入....
2   写入....
2 finished 写入....
3   写入....
3 finished 写入....
4   写入....
4 finished 写入....
0 读取.....
1 读取.....
3 读取.....
2 读取.....
4 读取.....
2 finished 读取.....
0 finished 读取.....
1 finished 读取.....
3 finished 读取.....
4 finished 读取.....

Write order is guaranteed, but read order is not guaranteed

CountDownLatch

Principle of use

  1. Create a CountDownLatch and set the counter value.
  2. Start multiple threads and call the countDown() method of the CountDownLatch instance.
  3. The main thread calls await()the method, so that the operation of the main thread will be blocked on this method until other threads complete their respective tasks, the count value is 0, stop blocking, and the main thread continues to execute.

scenes to be used

There are N tasks executing in a program. We can create a CountDownLatch with a value of N. When each task is completed, call a countDown()method to decrement count值, and then use the method in the main thread await()to wait for the task to complete, and the main thread continues to execute.

public class CountDownLatchDemo {
    
    

  public static void main(String[] args) throws InterruptedException {
    
    
    CountDownLatch countDownLatch = new CountDownLatch(6);
		// 等待所有人离开教室案例
    for (int i = 1; i <= 6; i++) {
    
    
      new Thread(() -> {
    
    
        System.out.println(Thread.currentThread().getName() + " 离开教室....");
        // * Decrements the count of the latch, releasing all waiting threads if
        // * the count reaches zero.
        countDownLatch.countDown();
      }, CountryEnum.getName(i)).start();
    }
    // Causes the current thread to wait until the latch has counted down to
    // * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
    countDownLatch.await();
    System.out.println(Thread.currentThread().getName() + " ********关闭");
    // closeDoor();
  }

  public static void closeDoor() throws InterruptedException {
    
    
    CountDownLatch countDownLatch = new CountDownLatch(6);

    for (int i = 0; i < 6; i++) {
    
    
      new Thread(() -> {
    
    
        System.out.println(Thread.currentThread().getName() + " 离开教室....");
        countDownLatch.countDown();
      }, String.valueOf(i)).start();
    }
    countDownLatch.await();
    System.out.println(Thread.currentThread().getName() + " ********关闭");
  }

  enum CountryEnum {
    
    
    ONE(1, "齐"), TWO(2, "楚"), THREE(3, "燕"), FOUR(4, "赵"), FIVE(5, "魏"), SIX(6, "韩");


    CountryEnum(Integer code, String msg) {
    
    
      this.code = code;
      this.msg = msg;
    }

    private Integer code;
    private String msg;

    public Integer getCode() {
    
    
      return code;
    }

    public String getMsg() {
    
    
      return msg;
    }

    public static String getName(int index) {
    
    
      CountryEnum[] values = CountryEnum.values();
      for (CountryEnum value : values) {
    
    
        if (index == value.getCode()) {
    
    
          return value.getMsg();
        }
      }
      return null;
    }
  }

}

CyclicBarrier

The 7 threads are random, and the callback function of cyclicBarrier will be executed after the execution is completed

  public static void main(String[] args) {
    
    
    CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()-> System.out.println("完成收集....."));

    for (int i = 0; i < 7; i++) {
    
    
      new Thread(() -> {
    
    
        System.out.println(Thread.currentThread().getName() + " 开始.....");

        try {
    
    
          cyclicBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
    
    
          e.printStackTrace();
        }
      }, String.valueOf(i)).start();
    }

  }

The difference between CyclicBarrier and CountDownLatch

  • The counter of CountDownLatch can only be used once, while the counter of CyclicBarrier can be reset using the reset() method and can be used multiple times, so CyclicBarrier can handle more complex scenarios;
  • CyclicBarrier also provides some other useful methods, such as the getNumberWaiting() method to obtain the number of threads blocked by CyclicBarrier, and the isBroken() method to know whether the blocked thread is interrupted;
  • CountDownLatch allows one or more threads to wait for a set of events to occur, while CyclicBarrier is used to wait for other threads to run to the barrier position.

Semaphore

Mutual exclusion of one or more resources. When using it, you need to construct a parameter to specify the number of shared resources. After the Semaphore is constructed, the Semaphore is acquired, and the Semaphore is released after the shared resources are used.

    // 模拟三个停车位
    Semaphore semaphore = new Semaphore(3);
    for (int i = 0; i < 6; i++) {
    
    
      new Thread(() -> {
    
    
        try {
    
    
          semaphore.acquire();
          System.out.println(Thread.currentThread().getName() + "  获得车位");
          TimeUnit.SECONDS.sleep(3);
          System.out.println(Thread.currentThread().getName() + "  离开");
        } catch (Exception e) {
    
    
          e.printStackTrace();
        } finally {
    
    
          semaphore.release();
        }
      }, String.valueOf(i)).start();
    }

blocking queue

BlockingQueue

Throw an exception special value block time out
insert Add(e) Offer put Offer
remove Remove() Pull Take Pull
examine Element() Peak - -

SynchronousQueue

A single-element queue, neither consumed nor produced

    SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();

    new Thread(() -> {
    
    
      try {
    
    
        System.out.println(Thread.currentThread().getName() + "put aa");
        synchronousQueue.put("aa");
        System.out.println(Thread.currentThread().getName() + "put bb");
        synchronousQueue.put("bb");
        System.out.println(Thread.currentThread().getName() + "put cc");
        synchronousQueue.put("cc");
      }catch (Exception e) {
    
    
        e.printStackTrace();
      }
    }, "aaa").start();

    new Thread(() -> {
    
    
      try {
    
    
        TimeUnit.SECONDS.sleep(5);
        System.out.println(synchronousQueue.take());
        TimeUnit.SECONDS.sleep(5);
        System.out.println(synchronousQueue.take());
        TimeUnit.SECONDS.sleep(5);
        System.out.println(synchronousQueue.take());
      }catch (Exception e) {
    
    
        e.printStackTrace();
      }
    }, "bbb").start();

Thread Communication Producer Consumer

class Sharedata {
    
    

  private int number = 0;
  private Lock lock = new ReentrantLock();
  private Condition condition = lock.newCondition();

  public void increment() {
    
    
    lock.lock();
    try {
    
    
      // 1.判断
      while (number != 0) {
    
    
        // 等待,不生产
        condition.await();
      }
      // 2.干活儿
      number++;
      System.out.println(Thread.currentThread().getName() + " 生产 " + number);
      // 3.唤醒
      condition.signal();
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      lock.unlock();
    }
  }

  public void decrement() {
    
    
    lock.lock();
    try {
    
    
      // 1.判断
      while (number == 0) {
    
    
        // 等待,不生产
        condition.await();
      }
      // 2.干活儿
      number--;
      System.out.println(Thread.currentThread().getName() + " 消费 " + number);
      // 3.唤醒
      condition.signal();
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      lock.unlock();
    }
  }
}

The judgment of multi-threaded while if is only suitable for the judgment of two threads

The difference between synchronized and lock

reference article

on the level of existence

synchronized: Java keyword, at the jvm level

Lock: is an interface

lock release

synchronized:

1. Execute the synchronization code with the thread that acquires the lock, and release the lock

2. When an exception occurs in thread execution, jvm will let the thread release the lock

Lock: The lock must be released in finally, otherwise it is easy to cause thread deadlock

lock acquisition

synchronized: Assume that thread A acquires the lock and thread B waits. If thread A is blocked, thread B will wait forever

Lock: It depends on the situation. Lock has multiple ways to acquire locks. Basically, you can try to acquire locks, and threads don't have to wait all the time (you can judge whether there is a lock through tryLock)

Lock release (deadlock generation)

synchronized: When an exception occurs, the lock will be automatically released, so there will be no deadlock

Lock: When an exception occurs, the occupied lock will not be released actively, and the lock must be manually unlocked to release the lock, which may cause deadlock

lock status

synchronized: Unable to judge

Lock: can judge

type of lock

synchronized: reentrant, uninterruptible, unfair

Lock: Reentrant, judgmental, and fair (both are possible)

performance

synchronized: a small amount of synchronization

Lock: Mass synchronization

  • Lock can improve the efficiency of multiple threads for read operations. (readwritelock can be used to achieve read-write separation)
  • When the resource competition is not very fierce, the performance of Synchronized is better than ReetrantLock, but in the case of fierce resource competition, the performance of Synchronized will drop dozens of times, but the performance of ReetrantLock can maintain normal;
  • ReentrantLock provides a variety of synchronization, such as time-limited synchronization, synchronization that can be interrupted (synchronized synchronization cannot be interrupted), etc. In the case of less competitive resources, the performance is slightly worse than synchronized. But when the synchronization is very intense, the performance of synchronized can drop dozens of times at once. And ReentrantLock can indeed maintain normality.

scheduling

synchronized: use the wait, notify, notifyAll scheduling mechanism of the Object object itself

Lock: Condition can be used for scheduling between threads

usage

Synchronized: Add this control to the object that needs to be synchronized. Synchronized can be added to the method or in a specific code block. The objects that need to be locked are indicated in parentheses.

Lock: Generally use the ReentrantLock class as a lock. Locking and unlocking need to be indicated through lock() and unlock(). Therefore, unlock() is generally written in the finally block to prevent deadlock.

underlying implementation

Synchronized: The bottom layer uses instruction codes to control the lock. Mapping into bytecode instructions is to add two instructions: monitorenter and monitorexit. When the thread execution encounters the monitorenter instruction, it will try to acquire the built-in lock. If the lock is acquired, the lock counter will be +1, and if the lock is not acquired, it will be blocked; when the monitorexit instruction is encountered, the lock counter will be -1. If the counter is 0, the lock will be released.

Lock: The bottom layer is the CAS optimistic lock, relying on the AbstractQueuedSynchronizer class to form all request threads into a CLH queue. All operations on the queue are performed through Lock-Free (CAS) operations.

cycle printing

Circular call of three threads a, b, c, each thread prints 10 times, which will be tested in some interviews

class Shareresource {
    
    

  // a=1,b=2,c=3
  private int number = 1;
  private Lock lock = new ReentrantLock();
  private Condition c1 = lock.newCondition();
  private Condition c2 = lock.newCondition();
  private Condition c3 = lock.newCondition();

  public void print5() {
    
    
    try {
    
    
      lock.lock();
      // 判断
      while (number != 1) {
    
    
        c1.await();
      }
      for (int i = 0; i < 5; i++) {
    
    
        System.out.println(Thread.currentThread().getName() + " " + i);
      }
      // 通知
      number = 2;
      c2.signal();
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      lock.unlock();
    }
  }

  public void print10() {
    
    
    try {
    
    
      lock.lock();
      while (number != 2) {
    
    
        c2.await();
      }
      for (int i = 0; i < 10; i++) {
    
    
        System.out.println(Thread.currentThread().getName() + " " + i);
      }
      // 通知
      number = 3;
      c3.signal();
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      lock.unlock();
    }
  }

  public void print15() {
    
    
    try {
    
    
      lock.lock();
      while (number != 3) {
    
    
        c3.await();
      }
      for (int i = 0; i < 15; i++) {
    
    
        System.out.println(Thread.currentThread().getName() + " " + i);
      }
      // 通知
      number = 1;
      c1.signal();
    } catch (Exception e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      lock.unlock();
    }
  }
}

Guess you like

Origin blog.csdn.net/weixin_55768452/article/details/132024036