这是我参与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中执行。这也是为了避免一旦执行的代码中抛出异常,能够正确的释放锁,而不是长期持有锁导致死锁。
后记
- 千古兴亡多少事?悠悠。不尽长江滚滚流。