-2

I'm getting into java multithreading, and decided to solve a more complex problem, but with little success so far.

I have a production line of workers that will try to get work (create a product) from a list:

  • i'm using executors to generate the worker threads;
  • each product creation implies the completion of a set of Tasks;
  • each Task has an associated time an resources it needs: exclusive access to each resource is needed, meaning it is impossible to use one in the creation of two products simultaneously.

The concurrent access to the work list was solved with notify-wait:

public class ProductionRequestQueue extends LinkedList<ProductionRequest> {

public ProductionRequestQueue() {
}

public synchronized void push(ProductionRequest productionRequest) {
    this.add(productionRequest);
    notifyAll();
}

public synchronized ProductionRequest pull() {
    while (this.size() == 0) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    return this.removeFirst();
}}

The problem is when i need to guarantee exclusive access to the resources. I have a list of the available resources in the production line, but i need to guarantee access to the resource it self and not the list. My workers does:

public Product createProduct(ProductionRequest productionRequest) {
    Product product = ProductionLine.products.get(productionRequest.getProductId());
    product.create();
    return product;
}

@Override
public void run() {
    ProductionRequest productionRequest;
    while (!productionRequestQueue.isEmpty()) {
        productionRequest = productionRequestQueue.pull();
        createProduct(productionRequest);
        System.out.println(productionRequest + " is being manufactured by " + this);
    }
}

Then i have a class that relates the resource with the time, where it simulates the production process by sleeping the thread:

public ResourceTime(Resource resource, int time) {
    this.resource = resource;
    this.time = time;
}

public void execute() {
    synchronized (this.resource) {
        Thread thread = new Thread(this);
        thread.start();
    }
}

@Override
public void run() {
    try {
        System.out.println(toString());
        Thread.sleep(Integer.valueOf(time));
    } catch (NumberFormatException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

I thought that the syncronized block would guarantee exclusive access to the resource, but i keep getting a null pointer exception:

Exception in thread "pool-1-thread-2" java.lang.NullPointerException
at production_line_sim.models.Worker.createProduct(Worker.java:14)
Any thoughts about this?

Also, how can i demonstrate that the synchronized block guarantees exclusive access to each resource? I think this solution may lead to a situation where some threads may not be able to access the resource, and some kind of fairness algorithm might be needed, can someone pinpoint me on how to do this?

raffah
  • 82
  • 1
  • 5
  • 1
    Why not just use the actual executors, tasks, pools, concurrent queues, etc provided by the standard library? – pvg Apr 30 '17 at 00:11
  • 1
    Well, not only is your question "What is a null pointer exception and how do I fix it?", you haven't even shown us the class `Worker` where the error occurs. – markspace Apr 30 '17 at 00:12
  • Well, has soon i've published this question i've found the origin of the null pointer exception. It was related with bad input data after all. But that was not the main point of my question, it some how got lost with my copy/paste. I've updated the question – raffah Apr 30 '17 at 00:27
  • @pvg as i'm starting with multithreading i thought it would be better to understand the underlying concepts before starting using the standard library. Either way i think the only change i could do, is to use some concurrent collection, like concurrentlinkedqueue, instead of my implementation of ProductionRequestQueue. – raffah Apr 30 '17 at 00:33
  • 2
    Actually you're definitely better off using the standard library. The concepts are higher level and the stuff works. It's usually a mistake to re-implement these from primitives and/or to use the primitives directly. If you want to gain an understanding, the source to java.util.concurrent is available, Doug Lea's book remains a useful reference. – pvg Apr 30 '17 at 00:40

1 Answers1

3

First of all, I would like to emphasize @pvg's advice:

Actually you're definitely better off using the standard library. The concepts are higher level and the stuff works. It's usually a mistake to re-implement these from primitives and/or to use the primitives directly. If you want to gain an understanding, the source to java.util.concurrent is available, Doug Lea's book remains a useful reference.


You say you have solved the NPE problem.

Also, how can i demonstrate that the synchronized block guarantees exclusive access to each resource?

The synchronized block will give you exclusive access among threads that are using an equivalent block. But if your codebase contains any code that uses a Resource instance without synchronized on that resource, then there is a risk of non-exclusive access, race conditions and memory anomalies.

How would you find such a code (that is breaking the rules)?

  1. Code inspection
  2. A static analysis tool that looks for concurrency hazards. (I can't recommend one ...)

I think this solution may lead to a situation where some threads may not be able to access the resource, and some kind of fairness algorithm might be needed, can someone pinpoint me on how to do this?

Fairness is always a potential problem, but generally we ignore it.

A more real problem ... that you cannot ignore ... is deadlock. In your case, consider the case where your need 2 or more "resources" to create a "product". Suppose you have two workers A and B, that do the following:

Worker A: synchronize R1
    Worker A: synchronize R2

Worker B: synchronize R2
    Worker B: synchronize R1

If those 2 sequences happen simultaneously, you can end up with A waiting for B to release R2 and B waiting for A to release R1.

There are strategies for dealing with this:

  1. Avoid it by arranging that workers acquire resource locks in the same order.
  2. Deadlock detection. For example, something external to the workers notices that when workers are deadlocked, and interrupts one of them (or similar). (However, beware of livelock ...)
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216