Java Lock

java.util.concurrent.locks包常用类及接口

  • LockReadWriteLock 是锁的根接口
  • Lock 接口的主要实现类是 ReentrantLock,ReadWriteLock的主要实现类是 ReentrantReadWriteLock
  • ReentrantReadWriteLock 包括两个成员内部类,ReadLock 和 WriteLock

Lock 接口

public interface lock {
  /**
   * 获取锁,如果锁已被其它线程获取则等待。
   */
  void lock();
  
  /**
   * 获取锁,如果锁已被其它线程获取则等待。
   * 以下两种情况线程会停止等待:
   * 1. 线程成功获取锁
   * 2. 响应中断,其它线程可以中断当前线程的等待状态
   */
  void lockInterruptibly() throws InterruptedException;
  
  /**
   * 获取锁,立即返回,不等待
   * @return true: 成功获取锁
   * @return false: 获取锁失败,锁已被其它线程占用
   */
  boolean tryLock();
  
  /**
   * 获取锁,在没有获取锁时会等待 time 时间
   * @return true: 一开始就成功获取锁 或 在等待期间内成功获取锁
   * @return false: 获取锁失败
   */
  boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  
  /**
   * 释放锁, 使用 Lock 必须手动释放锁
   */
  void unlock();
  
  /**
   * 返回一个绑定到Lock对象上的 Condition 实例
   * 在等待条件之前,当前线程必须持有锁
   * 调用 Condition.await() 方法会自动释放锁, 并在等待返回之前获取锁
   */
  Condition newCondition();
}

ReadWriteLock 接口

public interface ReadWriteLock {
  /**
   * 读锁共享
   * Returns the lock used for reading.
   * @return the lock used for reading
   */
  Lock readLock();

  /**
   * 写锁独占
   * Returns the lock used for writing.
   * @return the lock used for writing
   */
  Lock writeLock();
}

ReentrantLock 示例

Resource 资源类

public class Resource {
  private int num;
  
  public Resource() {}
  
  public Resource(int num) {
    this.num = num;
  }
  
  public int getNum() {
    return num;
  }
  
  public void setNum(int num) {
    this.num = num;
  }

  public void doSomething() {
    System.out.println("Do some operation.");
  }

  public void doLogging() {
    System.out.println("Do logging.");
  }
}

ReentrantLockExample 类

public class ReentrantLockExample implements Runnable{
  private Resource resource;
  private Lock lock;

  public ReentrantLockExample(Resource resource) {
    this.resource = resource;
    this.lock = new ReentrantLock();
  }

  @Override
  public void run() {
    // TODO Auto-generated method stub
    try {
      // 尝试获取锁
      if (lock.tryLock(10, TimeUnit.SECONDS)) {
        resource.doSomething();
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      lock.unlock();    // 释放锁
    }
    resource.doLogging();
  }

  public static void main(String[] args) {
    Runnable worker = new ReentrantLockExample(new Resource());
    new Thread(worker).start();
  }
}

ReentrantReadWriteLock 示例

ReadWriteLockExample 类

public class ReadWriteLockExample {
  private Resource resource;
  private ReadWriteLock readWriteLock;

  public ReadWriteLockExample(Resource resource) {
    this.resource = resource;
    this.readWriteLock = new ReentrantReadWriteLock();
  }

  public void read() {
    readWriteLock.readLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + " start to read data.");
      Thread.sleep(1000);
      System.out.println(Thread.currentThread().getName() + " end to read data: " + resource.getNum());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      readWriteLock.readLock().unlock();
    }
  }

  public void write(Resource resource) {
    readWriteLock.writeLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + " start to write data.");
      Thread.sleep((long) (Math.random() * 1000));
      this.resource = resource;
      System.out.println(Thread.currentThread().getName() + " end to write data: " + this.resource.getNum());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      readWriteLock.writeLock().unlock();
    }
  }

  public static void main(String[] args) {
    ReadWriteLockExample example = new ReadWriteLockExample(new Resource(0));

    // 分别启动5个读写数据线程
    for (int i = 0; i < 5; i++) {
      new Thread() {
        @Override
        public void run() {
          while (true) {
            example.read();
          }
        }
      }.start();

      new Thread() {
        @Override
        public void run() {
          while (true) {
            Resource resource = new Resource((int) (Math.random() * 1000));
            example.write(resource);
          }
        }
      }.start();
    }
  }
}

Synchronized

Synchronized 关键字获取锁

public class SynchronizedExample implements Runnable{
  private Resource resource;
  public SynchronizedExample(Resource resource) {
    this.resource = resource;
  }

  @Override
  public void run() {
    synchronized(resource) {
      resource.doSomething();
    }
    resource.doLogging();
  }
}

Synchronized 锁的是什么

class T {
  private Resource resource;
  
  public synchronized void methodA(){}
  public synchronized static void methodB(){}
  public void methodC() {
    synchronized(resource) {
      // Do something
    }
  }
}

// 上面的代码相当于
class T {
  public synchronized(this) void methodA(){}
  public synchronized(T.class) static void methodB(){}
  public void methodC() {
    synchronized(resource) {
      // Do something
    }
  }
}
  • 对于非静态方法,synchronized 锁的是this即实例对象
  • 对于静态方法,synchronized 锁的是T.class即类对象(每个类都有一个类对象)
  • 对于同步代码块,则锁的括号里的对象

Synchronized 与 Lock 的区别

  • Java Lock API 为锁提供了更多的可见性和选项,可以使用 tryLock() 方法保证线程只会等待等定的时间,而 synchronized 可能使线程无限制等待,产生死锁。
  • synchronized 代码块更加简洁,不需要手动释放锁;Lock则必须使用 try-finally 代码块来保证在发生异常情况下也能释放锁。
  • synchronized 方法或代码块只能覆盖一个方法;Lock则可以跨越多个方法。
  • synchronized 不支持公平锁;Lock则可以通过设置参数创建一个公平锁。
  • Lock可以创建不同的 Condition,不同的线程可以为不同的条件创建 await()。

参考文章:
[1] Java Lock

[2] Java锁Lock接口详解

[3] Synchronized锁的是什么

posted @ 2022-07-03 16:38  ylyzty  阅读(19)  评论(0编辑  收藏  举报