Home > front end >  How can I lock/unlock resources between threads?
How can I lock/unlock resources between threads?

Time:01-11

I'm playing with a broker API.

I have created a SSLSocket like this:

try {
    // Crea el socket SSL
    s = (SSLSocket) SSLSocketFactory.getDefault().createSocket("xapi.xtb.com", 5124);
    s.setKeepAlive(true);          
} catch (IOException e) {
    e.printStackTrace();
}

I need that socket to be used in login, and trade actions, but, also, I need to make a ping every 10 minutes.

So I have used thread to create one thread to login and start the ping loop (last one with a sleep(60000))

new Thread(() -> {
        try {
            assert s != null;
            BufferedWriter pingOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            BufferedReader pingIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
            // crea un bucle while infinito

            while (true) {
                pingOut.write(
                        "{\"command\":\"ping\"}");
                pingOut.flush();
                pingIn.readLine();
                Thread.sleep(10000);
            }
        } catch (IOException  ex) {
            ex.printStackTrace();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }).start();

And then, another thread started on a button click which sends a similar string, but with trade options and without loops, that thread (trade) is killed after every trade action.

My main problem, is that sometimes, bufferedRead mixes responses from both threads, so I thought of using locks, waiting and notifying, but this is not working, the ping loop is kidnapping the resource (sslsocket) locking access to the other thread (or in future, threads)

So, how can I implement it to use "s", but avoid to mix responses? (sometimes, when printing tradeIn.readline(), I got the pingIn.readline() reply content.

Or if there is another way to get the responses to avoid mixing, is also welcome

Adding synchronized attempt code:

Object lock = new Object();

new Thread(() -> {
        synchronized (lock) {
            try {
                assert s != null;
                BufferedWriter pingOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                BufferedReader pingIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
                // crea un bucle while infinito

                while (true) {
                    pingOut.write(
                            "{\"command\":\"ping\"}");
                    pingOut.flush();
                    pingIn.readLine();
                    Thread.sleep(10000);
                }
            } catch (IOException  ex) {
                ex.printStackTrace();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }

            lock.notify();
        }
    }).start();

The second thread:

 synchronized (lock) {
    try {
        lock.wait();
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }
}
try {
    assert s != null;
    BufferedReader tradeIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
    BufferedWriter tradeOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    tradeOut.write(
            "{\"command\":\"tradeTransaction\",\"arguments\":{\"tradeTransInfo\":{\"cmd\":0,\"customComment\":\"Some text\",\"expiration\":3462006335000,\"offset\":0,\"order\":0,\"price\":10000.0,\"sl\":9420.0,\"symbol\":\"US100\",\"tp\":0.0,\"type\":0,\"volume\":0.02}}}");
    tradeOut.flush();
    twoperacion.setText(tradeIn.readLine());
} catch (IOException ex) {
    ex.printStackTrace();
}

CodePudding user response:

It's because that's not how the wait/notify system should be used.

// first snippet
 synchronized (lock) {

As long as this thread's 'location' (what it is executing) is within the body of this synchronized block, any other thread, if it hits a synchronized (lock) statement, will wait. Only one thread can 'hold the lock'. With one exception: lock.wait().

// still first snippet
Thread.sleep(10000);

Calling sleep inside any synchronized block is virtually guaranteed to be a grave mistake. You are still holding the lock - 10 seconds long then, no other thread can enter synchronized (lock). I highly doubt that's what you want. Stop with the synchronized and pick it up again afterwards.

// second snippet
lock.wait();

wait is unique: It releases the lock, then waits for a notify. Once a notification is received, it will reacquire the lock (which will by definition be impossible; whatever thread did the notifying still holds it, as you cannot call lock.notify() unless you are inside synchronized(lock), hence, the other thread has the lock and the thread with the wait() command cannot reacquire it). This will take some time (the other thread needs to get out of the synchronized block first).

It will then continue.

Crucially then, the thread that does the notifying does need to stop being in the synchronized block at some point.

The general 'look' of a notifying block of code that runs on a simple timer is thus something like this:

while (running) {
  synchronized (lock) {
    // Do relevant stuff
    lock.notify();
  }
  Thread.sleep(10000);
}

Note how we do not hold that synchronized lock when sleeping.

Generally you're better off using something relevant from the java.util.concurrent package. Which might be a lock, but more often is a latch, or a collection, or anything else of the many, many things that package has to offer. I'm not quite clear on the underlying problem you're trying to solve, so I'm not sure what to recommend.

  • Related