欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Locks in Java

程序员文章站 2022-03-09 18:59:08
...

原文:http://tutorials.jenkov.com/java-concurrency/locks.html

A lock is a thread synchronization mechanism like synchronized blocks except locks can be more sophisticated than Java’s synchronized blocks. Locks (and other more advanced synchronization mechanisms) are created using synchronized blocks, so it is not like we can get totally rid of the synchronized keyword.

Lock 是一种线程synchronization机制,类似于 synchronized代码块,但是比其更加复杂。

A Simple Lock 简单例子

直接使用synchronized代码块

public class Counter {
    private int count = 0;
    public int inc() {
        synchronized(this){
            return ++count;
        }
    }
}

使用 Lock 代替 synchronized代码块

public class Counter {
    private Lock lock = new Lock();
    private int count = 0;
    public int inc() {
        try {
            lock.lock();
            return ++count;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

Lock类的一种简单实现,不具备锁的可重入

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();
    }
}

Lock Reentrance 可重入锁

一个线程可以多次进入已经获得过锁对象的同步块。

    public class Reentrant {
        public synchronized outer() {
            inner();
        }
        public synchronized inner() {
            //do something
        }
    }

Notice how both outer() and inner() are declared synchronized, which in Java is equivalent to a synchronized(this) block. If a thread calls outer() there is no problem calling inner() from inside outer(), since both methods (or blocks) are synchronized on the same monitor object (“this”). If a thread already holds the lock on a monitor object, it has access to all blocks synchronized on the same monitor object. This is called reentrance. The thread can reenter any block of code for which it already holds the lock.

inner和outer方法等价于 synchronized(this)代码块,monitor object都是this。如果一个线程已经获得了对象锁,那么它能访问拥有同一monitor object的同步代码块,这种就称为可重入。

之前上面的Lock例子不具备可重入。

    public class Reentrant2{
        Lock lock = new Lock();
        public void outer(){
            lock.lock();
            inner();
            lock.unlock();
        }
        public synchronized inner(){
            lock.lock();
            //do something
            lock.unlock();
        }
    }

调用outer方法,lock第一次能够锁住,执行到inner方法时,线程将再次去尝试锁住lock实例,这是不会成功的,因为在outer方法中已经locked了。

原因很明显,参看Lock类的代码

    public class Lock{
        boolean isLocked = false;
        public synchronized void lock()
                throws InterruptedException{
            while(isLocked){
                wait();
            }
            isLocked = true;
        }
  ...
    }

调用inner方法时,会执行wait方法,而调用outer方法已经持有了锁,这时就会一直等待下去。
下面是改进后的Lock类。

    public class Lock{
        boolean isLocked = false;
        Thread  lockedBy = null;
        int     lockedCount = 0;
        public synchronized void lock()
                throws InterruptedException{
            Thread callingThread = Thread.currentThread();
            while(isLocked && lockedBy != callingThread){
                // 对于当前线程不在执行线程等待
                wait();
            }
            isLocked = true;
            lockedCount++;
            lockedBy = callingThread;
        }
        public synchronized void unlock(){
            if(Thread.curentThread() == this.lockedBy){
                lockedCount--;
                if(lockedCount == 0){
                    isLocked = false;
                    notify();
                }
            }
        }
  ...
    }

Lock Fairness 锁的公平性

Java’s synchronized blocks makes no guarantees about the sequence in which threads trying to enter them are granted access. Therefore, if many threads are constantly competing for access to the same synchronized block, there is a risk that one or more of the threads are never granted access - that access is always granted to other threads. This is called starvation. To avoid this a Lock should be fair. Since the Lock implementations shown in this text uses synchronized blocks internally, they do not guarantee fairness. Starvation and fairness are discussed in more detail in the text Starvation and Fairness.

Calling unlock() From a finally-clause

锁的释放应该放在finally中。

    lock.lock();
    try{
        // do somethings,which may throw exception
    } finally {
        lock.unlock();
    }
相关标签: lock