内卷老员工之java中的锁浅谈

138 阅读2分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

java中的锁

  • 锁是一种利用synchronized创建的同步机制,类似于直接使用synchronized关键字,但比其更复杂。java.util.concurrent.locks中包含了多种锁实现,因此多数情况下可以不必自定义自己的锁。

简单的锁

private int count = 0;
public int inc(){
    synchronized(this){
        return ++count;
    }
}
  • 上面的代码每次只有一个线程能够执行实际的代码块,实现线程锁的效果。
private Lock lock = new Lock();
  private int count = 0;

  public int inc(){
    lock.lock();
    int newCount = ++count;
    lock.unlock();
    return newCount;
  }
public class Lock{

  private boolean isLocked = false;

  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}
  • 如上为用自定义的锁取代了synchronized关键字实现线程的锁定效果。在自定义的lock类中,采用synchronized关键字将锁定方法与解锁方法进行锁定,因此同时只有各一个线程执行锁定及解锁的代码块。
  • 而在while循环中,可以认为是一个自旋锁。当锁定信号量为true时,即已经被锁定,锁定中的代码块便会进入自旋等待。一直到某一刻当其他线程执行解锁代码将变量重置为false时,锁定线程会继续执行,并变更锁定信号量的状态。wait()及notify()进行了锁定及解锁代码之间的锁释放。解锁与加锁中synchronized方法修饰的是方法,也就是等同于锁定了实例,因此需要wait()和notify()进行唤醒通信。

可重入锁

  • java的synchronized是可重入锁。可重入锁意味着当java线程执行到一个同步代码块中时,锁定了该对象,该线程可以进入同一个监控对象下的其他同步代码块。
synchronized void start(){
    this.end();
}

synchronized void end(){
    
}
  • 这段代码锁定的对象,理论上都是this,也就是该实例对象。当线程对start()方法进行调用时,由于两个方法持有同一个锁定实例对象,因此它可以访问end()中的代码块,而这就是重入。

公平锁

  • java的synchronized不保证进入代码块的线程的执行顺序,因此可能存在线程等待执行却永远竞争不过其他线程,形成线程饥饿的问题。因此理论上为了避免这种问题,锁应该是公平的。
  • 除去之前文章曾提到过的锁公平方法外,还要注意解锁需要和保证在finally中执行。这也是为了避免一旦执行的代码中抛出异常,能够正确的释放锁,而不是长期持有锁导致死锁。

后记

  • 千古兴亡多少事?悠悠。不尽长江滚滚流。