1

I have synchronisation block in syncCmd function:

public Object Sync = new Object();
public void syncCmd(String controlCmd) {
    synchronized(Sync) {

        ...
    }
} 

I need to add some logic in case if one thread has occupied Sync and doing its job. In this case I would like to report "too busy" to system and not get to queue. What is the best way to know if somebody has occupied Sync section? How to know how many threads is waiting in this section? Everything is in Java 1.4.

vico
  • 17,051
  • 45
  • 159
  • 315
  • Hello, while [this](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) may not answer your question, believe it is still relevant in your case (JDK 1.4), unless you've already considered it as part of your implementation. – Ravindra HV Jul 30 '16 at 08:25
  • If any thread in your program can occupy a mutex for so long that other threads can't afford to wait for it, then that's an algorithm that's crying out to be redesigned. – Solomon Slow Jul 30 '16 at 16:22

4 Answers4

2

Have a look at the Lock interface and its implementation ReentrantLock. It allows you to use tryLock() method, including the variant that allows to wait for some time if the resource is already locked:

private  ReentrantLock lock = new ReentrantLock();

public void syncCmd(String controlCmd) {
    if (lock.tryLock()) {
        try {
            // Use your synchronized resource here
        } finally {
            lock.unlock();
        }
    } else {
        // Failed to lock
    }
}

Java 1.4, unfortunately, has no java.util.concurrency package and I think the best choice you have is to implement the same logic by means of synchronized and double checks:

public class Lock {
    private final Object lock = new Object();
    private volatile boolean locked = false;

    public boolean tryLock() {
        if (!locked) {
            synchronized (lock) {
                if (!locked) {
                    locked = true;
                    return true;
                }
            }
        }
        return false;
    }

    public void unlock() {
        synchronized (lock) {
            locked = false;
        }
    }
}

It will not work as fast as ReentrantLock that uses CAS loop backed by processor instructions in modern JVMs, but it will do the job.

This implementation is also not reentrant, you can extend it to track the locking thread and locks count if you need reentrance.

Important update: @Stephen C made a good point that double check is broken in Java 1.4 and one always must keep it in mind. But there're exceptions. For instance, short primitive types. So, I think it will work in this particular case. For more details, please, look at the "Double-Checked Locking is Broken" Declaration.

Andrew Lygin
  • 6,077
  • 1
  • 32
  • 37
  • Looks nice , but it is since java 1.5 – vico Jul 30 '16 at 07:42
  • @Roman, I agree that I chose bad wording here. It doesn't have to use, I'll edit the comment. It depends on the chosen fairness policy, but in general it plays alone. – Andrew Lygin Jul 30 '16 at 08:03
  • @vico, you're right, I've expanded my answer. – Andrew Lygin Jul 30 '16 at 08:18
  • **WARNING** - Double-checked locking is broken in Java 1.4, even if you use `volatile`. https://en.wikipedia.org/wiki/Double-checked_locking – Stephen C Jul 30 '16 at 08:49
  • @Stephen C, as far as I know, it's broken for object references, but works for 32-bit (or less) primitive types, so it can be used in this particular implementation. Here's the detailed explanation: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html – Andrew Lygin Jul 30 '16 at 08:58
1

Synchronized blocks / methods and primitive mutexes can't do that in Java.

But if you use a Lock instead (javadoc), you can use tryLock either to never block or to only block for a limited time.

Example:

 Lock l = new ReentrantLock();
 if (l.tryLock()) {
     try {
       // access the resource protected by this lock
     } finally {
           l.unlock();
     }
 else {
     // report "too busy"
 }

But note that it is essential to use "try ... finally" and an explicit unlock() call to ensure that the lock is always released. (Unlike the synchronized constructs, which takes care of that for you automatically.)

Prior to Java 1.5 there is no solution that I am aware of in pure Java. It might be possible with native code trickery, but I don't know how.


You / your management should be looking to ditch support in your products for Java 1.4, and to migrating away from any third-party product that depends on top of it. Java 1.5 itself was EOL'd many years ago. In fact, all releases prior to Java 1.8 have been EOL'd; see the Oracle Java SE Support Roadmap document.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

Two of the answers above talked about java.util.concurrent.locks.ReentrantLock, but it doesn't exist in Java 1.4.

Too bad so sad?

No! If system libraries and 3rd party libraries don't hand you what you want, then write it yourself!

The code below does what you asked for, and absolutely nothing more. I personally would not use it without first adding some features that would make it more useable, more testable, and most importantly, more foolproof.

I'm just offering it to you as an example of where to begin.

public class ExtremelySimplisticNonReentrantLock {
    boolean isLocked = false;

    /**
     * @return true if the lock was acquired, false otherwise.
     */
    public synchronized boolean tryToAcquire() {
        if (isLocked) {
            return false;
        }
        isLocked = true;
        return true;
    }

    public synchronized void release() {
        lsLocked = false;
    }
}

Share and Enjoy!

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
0

Try this (Two classes - Executor and Tracker ) :

Executor :

package com.example.so.jdk1_4.synch;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;


/**
 * <p> For http://stackoverflow.com/questions/38671520/not-wait-in-case-synchronized-section-is-occupied </p>
 * @author Ravindra HV
 */
public class InUseExample {

    public synchronized void execute(String command) {

        InUseTracker.obtainClassInstance().setInuse(true);

        try {
            System.out.println("Executing :"+command);
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }// do work

        InUseTracker.obtainClassInstance().setInuse(false);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("Start :"+new Date());
        testInUseExample();
        System.out.println("Final wait count :"+InUseTracker.obtainClassInstance().waitCount());
        System.out.println("End :"+new Date());
    }


    private static void testInUseExample() {
        final InUseExample inUseExample = new InUseExample();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {

                try {
                    InUseTracker.obtainClassInstance().incrementWaitCount();
                    while(true) {
                        if( InUseTracker.obtainClassInstance().isInuse() == false ) { // reduces the chances of this thread going to a block mode..
                            inUseExample.execute(Thread.currentThread().getName());
                            break;
                        }
                        else {
                            try {
                                Random random = new Random();
                                String message = Thread.currentThread().getName()+" - block in use by :"+InUseTracker.obtainClassInstance().getInUseBy();
                                message = message+" "+". Wait Count :"+InUseTracker.obtainClassInstance().waitCount();
                                System.out.println(message);
                                Thread.sleep(random.nextInt(1000));
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    InUseTracker.obtainClassInstance().decrementWaitCount();
                }

            }
        };

        int threadCount = 10;
        List<Thread> threadPoolTemp = new ArrayList<Thread>();
        for(int i=0;i<threadCount;i++) {
            Thread thread = new Thread(runnable);
            threadPoolTemp.add(thread);
        }

        for (Thread thread : threadPoolTemp) {
            thread.start();
        }

        for (Thread thread : threadPoolTemp) {
            try {
                thread.join(); // wait until all threads have executed..
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }



}

Tracker :

package com.example.so.jdk1_4.synch;

/**
 * <p> For http://stackoverflow.com/questions/38671520/not-wait-in-case-synchronized-section-is-occupied </p>
 * @author Ravindra HV
 */
public class InUseTracker {

    private boolean inuse;
    private int waitCount;
    private String inUseBy;

    private static InUseTracker DEFAULT_INSTANCE = new InUseTracker();

    private InUseTracker() {
    }

    public static InUseTracker obtainClassInstance() {
        return DEFAULT_INSTANCE;
    }


    public synchronized boolean isInuse() {
        return inuse;
    }

    public synchronized void setInuse(boolean inuse) {
        this.inuse = inuse;
        if(inuse) {
            setInUseBy(Thread.currentThread().getName());   
        }
        else {
            setInUseBy("");
        }

    }

    private void setInUseBy(String inUseBy) {
        this.inUseBy = inUseBy;
    }

    public synchronized String getInUseBy() {
        return inUseBy;
    }

    public synchronized void incrementWaitCount() {
        waitCount++;
    }

    public synchronized void decrementWaitCount() {
        waitCount--;
    }

    public synchronized int waitCount() {
        return waitCount;
    }

}

PS: Guess you'd have to move the

InUseTracker.obtainClassInstance().setInuse(false);

within a finally if or as appropriate.

Ravindra HV
  • 2,558
  • 1
  • 17
  • 26