3

Javadoc and some answers(Threads - Why a Lock has to be followed by try and finally) state that:

In most cases, the following idiom should be used:

 Lock l = ...;
 l.lock();
 try {
     // access the resource protected by this lock
 } finally {
     l.unlock();
 }

I have seen examples of this idiom in standard Java libraries. This is my example of using it. Fields acc1 and acc2 represent a wellknown example of bank accounts. The main constraint is the sum of values of acc's - it should be 0.

public class Main {
    int acc1;
    int acc2;

    ReadWriteLock lock = new ReentrantReadWriteLock();

    public int readSum() {
        lock.readLock().lock();
        try {
            return acc1 + acc2;
        } finally {
            lock.readLock().unlock();
        }
    }

    public void transfer() {
        lock.writeLock().lock();
        try {
            acc1--; // constraint is corrupted
                    // exception throwed here
            acc2++; // constraint is regained
        } finally {
            lock.writeLock().unlock();
        }
    }
}

I understand using the idiom at read case: if exception thrown in read method other threads still can read/write consistent resource. But if excteption thrown in write method read methods can read inconsisted resource.

Why reading inconsistent values is more preferable then infinity lock waiting? Why Java libraries authors prefer this behavior?

Community
  • 1
  • 1
osseum
  • 187
  • 14
  • locks provide some forms of transactional behavior (e.g. consistent views of data). however, they don't provide rollback functionality. you either need to implement that yourself or use a database. – jtahlborn Jul 31 '16 at 13:22
  • When your software runs a commercial web site that transacts millions of dollars worth of business every day, your customers will not be happy if it reacts to an I/O error by hanging forever. (Don't ask me how I know!) – Solomon Slow Jul 31 '16 at 16:40
  • P.S.: @GhostCat's answer talks about what Robert ("Uncle Bob") Martin calls _separation of concerns_---the idea that, if you make one blob of code responsible for solving two or more different problems (e.g., locking _and_ error handling), it will turn into a maintenance nightmare in the future when the requirements change. Check out Uncle Bob's book: https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ – Solomon Slow Jul 31 '16 at 16:59
  • @james large IOException is checked it is not surprise for you. You should catch it, rollback resource state and than call unlock. But if OutOfMemoryError would be thrown in ConcurrentHashMap.put method it just unlock map and allow other threads to use corrupted hashMap. – osseum Jul 31 '16 at 21:45
  • When I get OOME I expect a service denied but not a data corruption. – osseum Jul 31 '16 at 22:02
  • OK, sure. When you said "inconsisted resource," I mistakenly assumed that you were worried about corrupting a _persistent_ resource. But, that doesn't change what I said about separation of concerns. You're wanting to kill two birds with one stone. If you could do that in a mechanical design, I would call it "elegant." In software, I've learned to call it a headache waiting to happen. – Solomon Slow Aug 01 '16 at 00:29

2 Answers2

1

You are mixing up different concepts here. There is:

  1. Locking, and the preventing of dead-locks, and then
  2. Another dimension, lets call it "data integrity".

The point: those two are basically orthogonal. The fact that you are addressing one of them doesn't magically resolve the other for you!

Even when you look at your own example, you find that you put a try/finally there. But there is no catch there! at all cost Meaning: if some exception is thrown, that exception is still thrown, and some catcher will have to deal with it.

In other words: if your "locked" code can run into exceptions, then it is your responsibility to handle that in the way that makes the most sense.

And, from a "systems" view: when you got an indefinite lock, that will sooner or later degrade your whole system. If you run into that exception once, then you will run into it more often. So, chances are, that you will run out of threads/locks soon; and your whole application will be affected. That is exactly the kind of problem that can take down a distributed infrastructure - one component stopping to process incoming requests.

Long story short: infinite lock-waiting is something that you want to prevent, because it can seriously impact the ability of your application to function!

Finally: of course, this is about balancing of different requirements. But, example: assume your online shop looses the information that you just deleted an item from your shopping cart. Yes, that is annoying for the customer. But compare that to: the whole online shopping application stops handling requests; because of locking issues. Which problem will hurt your business more?

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • I think this is incorrent comparation. You can find out that money from your account gone or you find out that it blocked along some time. – osseum Jul 31 '16 at 21:52
0

You can rollback or give some warning info to user but you can do nothing if program is blocked.

I agree with what you talk about data consistency. It's dangerous to unlock in finally part without any rollback operation or warning.

Eugene
  • 10,627
  • 5
  • 49
  • 67
  • Thread which throw exception can give some warning. It is not blocked anyway. – osseum Jul 31 '16 at 13:48
  • @user3517035. If the thread gives warning but doesn't unlock the lock it has, it will block other thread. Like this example: http://stackoverflow.com/a/6950124/3378204I don't know what do you mean by "It is not blocked anyway". – Eugene Jul 31 '16 at 13:54