zoukankan      html  css  js  c++  java
  • java 锁

    Synchronized和Lock的区别

    原始构造
    1. synchronized是关键子,属于jvm层面
    2. Lock是具体类,属于api层面的锁
    使用方法
    1. synchronized不需要用户手动释放锁,执行完代码后会自动释放对锁的占用
    2. lock需要手动释放锁
    等待是否可中断
    1. synchronized不可中断,除非抛出异常或者正常运行完成
    2. lock可中断,1. 设置超时时间tryLock(long time,TimeUnit unit),2. lockInterruptibly()代码块中,调用interrupt()方法可中断
    加锁是否公平
    1. synchronized不公平
    2. ReentrantLock默认不公平,构造方法传入true,公平,传入false,不公平
    锁绑定多个条件condition
    1. synchronized 没有

    2. ReentrantLock使用多个condition用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized一样要么随便唤醒一个线程,要么唤醒全部线程

      /**
       * 多线程之间按顺序调用,实现A-->B-->C三个线程启动,要求如下:
       * AA打印5次,BB打印10次,CC打印15次
       * 循环10轮
       */
      public class SyncAndReentrantLockDemo {
          public static void main(String[] args) {
      
              ShareResource shareResource = new ShareResource();
              new Thread(() -> {
                  for (int i = 0; i < 10; i++) {
                      shareResource.print5();
                  }
              }, "AA").start();
              new Thread(() -> {
                  for (int i = 0; i < 10; i++) {
                      shareResource.print10();
                  }
              }, "BB").start();
              new Thread(() -> {
                  for (int i = 0; i < 15; i++) {
                      shareResource.print15();
                  }
              }, "CC").start();
          }
      }
      
      class ShareResource {
          private  int number = 1;
          private Lock lock = new ReentrantLock();
          Condition c1 = lock.newCondition();
          Condition c2 = lock.newCondition();
          Condition c3 = lock.newCondition();
      
          public void print5() {
              lock.lock();
              try {
                  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() {
              lock.lock();
              try {
                  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() {
              lock.lock();
              try {
                  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();
              }
          }
      }
      

    公平锁,非公平锁

    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:可伸缩,对统一资源初始化一个锁池,需要使用时从锁池获取锁,使用完后释放锁还给锁池,若锁被用完,则等待其它线程释放锁
  • 相关阅读:
    iOS开发Xcode7真机调试教程
    tableView 局部刷新
    CocoaPods 安装
    Mac OS
    iOS
    NSFileManager
    Label Font 字体样式设置
    iOS项目之企业证书打包和发布
    React Native学习之自定义Navigator
    React Native学习之DeviceEventEmitter传值
  • 原文地址:https://www.cnblogs.com/yloved/p/11835195.html
Copyright © 2011-2022 走看看