java 锁

Posted new 个对象()

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 锁相关的知识,希望对你有一定的参考价值。

公平锁,非公平锁

  1. 公平锁:在多线程环境下,按照申请锁的顺序获得锁
  2. 非公平锁:在多线程环境下,不一定按照申请锁的顺序获得锁,后申请锁的线程可以优先获得锁,有可能造成优先级反转或者饥饿现象
  3. 在ReentrantLock中,可以通过构造函数中的boolean值来指定是否为公平锁,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大

           Lock lock=new ReentrantLock(true);//公平锁
           Lock lock1=new ReentrantLock();//非公平锁,默认为false 
  4. synchronized是一种非公平锁

可重入锁(又名递归锁)

  1. 线程可以进入任何一个他已经拥有的锁所同步着的代码
  2. ReentrantLock/synchronized就是典型的可重入锁
  3. 可重入锁的最大作用是避免死锁
  4. 代码验证ReentrantLock/synchronized是可重入锁

    public class LockDemo2 {
        public static void main(String[] args) {
            Person person = new Person();
            new Thread(() -> {
                person.fun1();
            }, "t1").start();
    
            new Thread(person, "t2").start();
        }
    }
    
    class Person implements Runnable {
        //fun1,fun2验证synchronized是可重入锁
        public synchronized void fun1() {
            System.out.println(Thread.currentThread().getName() + "	 invoked fun1");
            fun2();
        }
    
        public synchronized void fun2() {
            System.out.println(Thread.currentThread().getName() + "	 invoked fun2");
        }
    
    
        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            fun3();
        }
    
        //验证ReentrantLock是可重入锁
        Lock lock = new ReentrantLock();
    
        public void fun3() {
    
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "	 invoked fun3");
                fun4();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    
        }
    
        public void fun4() {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "	 invoked fun4");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

自旋锁

  1. 定义:尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获得锁
  2. 好处:减少线程上下文切换的消耗
  3. 坏处:循环会消耗cpu
  4. 实现一个自旋锁(CAS实现)

    /**
    * 实现一个自旋锁
    * 自旋锁好处:循环比较获取直到成功为止,没有类似wait的阻塞
    * <p>
    * 通过CAS循环比较的方式完成自旋锁,A线程先获取锁,等待5秒,B线程进来时发现锁被使用,一直循环等待直到A释放锁
    */
    public class SpinLockDemo {
       AtomicReference<Thread> atomicReference = new AtomicReference<>();
    
       /**
        * 获取锁
        * 1. 判断原子引用值是否为空
        * 2. 若为空,则将原子引用值设置为当前线程
        * 3. 若不为空,循环等待值为空
        */
       public void myLock() {
           Thread thread = Thread.currentThread();
           while (!(atomicReference.compareAndSet(null, thread))) {
               //自旋锁循环获取锁
               System.out.println(thread.getName() + "	 正在自旋循环尝试获取锁");
    
           }
           System.out.println(thread.getName() + "	 lock");
       }
    
    
       /**
        * 释放锁
        * 1. 将原子引用值设置为空
        */
       public void myUnLock() {
           Thread thread = Thread.currentThread();
           atomicReference.compareAndSet(thread, null);
           System.out.println(thread.getName() + "	 unlock");
       }
    
       public static void main(String[] args) throws InterruptedException {
    
           SpinLockDemo spinLockDemo = new SpinLockDemo();
           new Thread(() -> {
               spinLockDemo.myLock();
               try {
                   TimeUnit.SECONDS.sleep(5);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               } finally {
                   spinLockDemo.myUnLock();
               }
    
           }, "A").start();
    
           //保证上面的线程先执行
           TimeUnit.SECONDS.sleep(1);
    
           new Thread(() -> {
               //5秒之后才能获取到锁
               spinLockDemo.myLock();
               spinLockDemo.myUnLock();
           }, "B").start();
       }
    }
    

读写锁/独占锁/共享锁

  1. 独占锁:该锁一次只能被一个线程持有。ReentrantLock,Synchronized都是独占锁
  2. 共享锁:该锁可被多个线程持有
  3. 对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
  4. 读写锁代码示例

    package com.yls.thread.lock;
    
    import java.util.HashMap;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
    * 当一个线程对某个资源进行写操作时,应该是一个整体(原子+独占),不能被其他线程打断
    */
    public class ReadWriteLockDemo {
    
       public static void main(String[] args) throws InterruptedException {
    
           MyCache myCache = new MyCache();
           for (int i = 0; i < 5; i++) {
               int finalI = i;
               new Thread(() -> {
                   myCache.put(finalI, finalI);
               }).start();
           }
           Thread.sleep(1);
           for (int i = 0; i < 5; i++) {
               int finalI = i;
               new Thread(() -> {
                   myCache.get(finalI);
               }).start();
           }
    
       }
    }
    
    //模拟缓存
    class MyCache {
       private HashMap<Integer, Integer> hashMap = new HashMap<>();
       private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
       //缓存中插入值,使用独占锁
       public void put(int key, int value) {
           readWriteLock.writeLock().lock();
           System.out.println(key + "准备放入:");
           hashMap.put(key, value);
           System.out.println(key + "放入成功");
           readWriteLock.writeLock().unlock();
       }
    
       //获取数据,共享锁
       public int get(int key) {
           readWriteLock.readLock().lock();
           System.out.println("准备获取" + key + ":");
           int value = hashMap.get(key);
           System.out.println(key + "获取成功");
           readWriteLock.readLock().unlock();
           return value;
       }
    
    
       public void clear() {
           hashMap.clear();
       }
    }

CountDownLatch,CyclicBarrier,SemaPhore

  1. CountDownLatch:减为0时,执行后面的线程
  2. CyclicBarrier:加到某一个值时,执行指定的线程
  3. SemaPhore:可伸缩,对统一资源初始化一个锁池,需要使用时从锁池获取锁,使用完后释放锁还给锁池,若锁被用完,则等待其它线程释放锁

以上是关于java 锁的主要内容,如果未能解决你的问题,请参考以下文章

java中ReentrantReadWriteLock读写锁的使用

synchronized学习

JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段

#yyds干货盘点# Java | 关于synchronized相关理解

为啥基于锁的程序不能组成正确的线程安全片段?

Java多线程——Lock&Condition