如何显式保持对java的锁定

时间:2017-08-09 01:05:40

标签: java multithreading synchronization

我有一个可以从多个线程访问的对象。我想实现它,以便访问其setter和getter,调用者必须先显式锁定它,然后在完成后解锁它。我虽然使用了同步方法,但与java的更明确的锁定API相比,它看起来并不简单。这是我目前使用ReentrantLock的存根实现。

public class Data {
    private ReentrantLock lock;

    private int IntValue;

    public Data() {
        this.IntValue = 0;
        this.lock = new ReentrantLock();
    }


    public void Lock() {
        lock.lock();
    }

    public void Unlock() {
        if (!lock.isLocked()) {
            return; 
        }
        //only the thread owning the lock can proceed to unlock
        lock.lock(); 
        int lockCount = lock.getHoldCount();
        for (int i = 0; i < lockCount; i++) {
            lock.unlock();
        }
    }

    public void SetVal(int val) {
        if (!lock.isLocked()) {
            return; 
        }
        lock.lock();
        this.IntValue = val;
    }
}

因此,如果一个线程想要调用SetVal(int val),它首先必须调用Lock()然后在完成后调用Unlock()。我已经在其setter / getter方法中放置了isLocked()检查来强制执行此规则。并且我在解锁时添加了一个额外的锁定调用,以确保只有拥有锁的线程可以继续解锁(ReentrackLock的一个独特功能)。在调用Unlock()方法之前,可以多次调用对象的setter / getter。因此,在Unlock()方法中,我必须遍历其HoldCount并为每个计数解锁。

我想知道是否有更有效和惯用的方法来实现这一目标?

2 个答案:

答案 0 :(得分:0)

如果您只使用int值,那么可以转到AtomicInteger

或使所有方法同步,以防止竞争条件。

或者如果你想同时支持synchronized和normal,那么你可以使用像collections.SynchronizedSet.Hope这样的包装器,这会有所帮助。

答案 1 :(得分:0)

你的方法正朝着错误的方向发展。 OOP范例规定所有数据都应该在内部保存和管理,但是你要做的是通过将内部数据提供给调用者来对外部数据进行外部化。

一个好的设计会试图隐藏这样一个事实,即锁定甚至是必要的,并在内部进行以使调用者免于执行此类任务。特别是因为如果您将正确锁定的责任转移给调用者,那么每个调用者都可能成为潜在的线程问题。但是,如果您在内部锁定,则只有一个潜在错误来源,因此如果您遇到问题,您就知道该去哪里看。

这就是你如何正确地处理OOP范式:

public class Data {
    // protected so it is accessible to derived classes
    // final so the lock object cannot be (accidentally) reassigned
    // Lock (base class) so it is easier to change the implementation later
    protected final Lock lock;

    // clear naming
    private int value;

    public Data() {
        // value is automatically initialized with 0
        this.lock = new ReentrantLock();
    }

    // by convention the setter for ... is set...
    public void setValue(final int value) {
        this.lock.lock();
       // absolutely use try/finally here, to ensure it is unlocked in all cases
        try {
            this.value = value;
        } finally {  
            this.lock.unlock();
        }
    }

    // by convention the getter for ... is get...
    public int getValue() {
        this.lock.lock();
        try {
            return this.value;
        } finally {
            this.lock.unlock();
        }
    }
}