简体   繁体   中英

Releasing a lock as soon as it is passed

I have a client-server architecture where the server has its clients do work in turns, passing a parameter each time a client is supposed to do work. The client does work with the parameter, and when it is done, the parameter becomes "invalid" and can no longer be used to do work.

I'd like to avoid running the garbage collector while this is going on, and so I'm avoiding object allocations. So the plan is that the server will associate a single parameter object with each client, and will always pass the same parameter every time the client is asked to do work. However, this creates the problem that the parameter will have to be re-set to "valid" while also ensuring that the client (who may have kept a reference to the parameter from the last time around) can't start using it (say, in a different thread) before it is asked to begin doing work.

So all of the parameter's public methods are synchronized, and the "valid" state is set, followed by a (synchronous) beginWork call to the client, inside a synchronized block. But this creates the problem that the client will unknowingly hold the parameter's lock, which could cause problems if the client wants to split its work into multiple threads. So I introduced a single-threaded ExecutorService which the server uses to fork off the call to beginWork , which ensures that the server will release the lock promptly. But this seems like a bad design to me -- why should this class need a whole other thread?

So my question is: given everything I've just laid out, have I made some horrible design mistake that has caused me to overcomplicate this class, or does it really need to be this complex?

interface Client {
    public void doWork(Param p);
}

interface Param {
    public boolean isValid();
}

class Server {
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private final MyParam[] params;

    private class MyParam implements Param {
        boolean isValid;
        Client client;
        Runnable task = new Runnable() {
            @Override
            public void run() {
                client.doWork(MyParam.this);
            }
        }

        @Override
        public synchronized boolean isValid() {
            return isValid;
        }
    }

    public void runClients() {
        while (true) {
            for (MyParam param : params) {
                synchronized(param) {
                    param.isValid = true;
                    // fork the client so we release the lock promptly (ugly!)
                    executor.execute(param.task);
                }

                // ... wait for the client to finish ...
            }
        }
    }
}

I fixed this by adding an explicit ReentrantLock to the Param interface. So instead of using a synchronized block, I just lock the param, and let the client unlock it if he wants to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM