0
votes

I am trying to access variables from multiple running threads.

I have a main class that starts up 2 threads, a producer and a consumer.

the PRODUCER thread reads a binary file. For each line in that binary file the producer thread creates an object from it, and passes that object to the consumer thread through a blockingqueue.

the CONSUMER then takes that object passed in through a blocking queue and outputs the values of the fields in that object to a text file.

sometimes there are errors in the binary file that the producer thread is reading from.

when there are too many errors in the binary file, I want the consumer thread to change the extension of the txt file that it outputs to .err

My problem: I dont know how to modify a value from a producer thread in a consumer thread. Ive been reading that I could use a volatile field. But I dont know what is the proper way to use it between threads.

Here is a very shortened and less complex example of my code :

public class Main 
{

   private volatile static boolean tooManyErrors= false; 

    public static void main(String[] args) 
   {

        BlockingQueue<binaryObject> queue = new LinkedBlockingQueue<>(null);

        binaryObject poison = null;

        new Thread(new Producer(tooManyErrors, queue, poison)).start();
        new Thread(new Consumer(tooManyErrors, queue, poison)).start();

    }
}

public class Producer implements Runnable 
{

    private final BlockingQueue<binaryObject> queue;
    private final binaryObject POISON;
    private boolean tooManyErrors;

    private int errorsCounter = 0;

    public Producer(boolean tooManyErrors, BlockingQueue<binaryObject> queue, 
    binaryObject POISON) 
    {
        this.tooManyErrors = tooManyErrors;
        this.queue = queue;
        this.POISON = POISON;
    }

    @Override
    public void run() 
    {

        try 
        {
            process();
        } 
        catch (InterruptedException e) 
        {
            Thread.currentThread().interrupt();
        }
        finally 
        {
            while (true) 
        {
                try 
                {
                    queue.put(POISON);
                    break;
                } 
                catch (InterruptedException e) 
                {
                    //...
                }
            }
        }

    }

    private void process() throws InterruptedException 
    {
       //here is where all the logic to read the file and create
       //the object goes in. counts the number of errors in the file
       //if too many errors, want to change the tooManyErrors to true       

       if(errorsCounter > 100)
       {
          tooManyErrors = true;
       }

    }

}


public class Consumer implements Runnable 
{

    private final BlockingQueue<binaryObject> queue;
    private final binaryObject POISON;
    private boolean tooManyErrors;

    //variable with extension name
    private String extension;

    public Consumer(boolean tooManyErrors, BlockingQueue<Integer> queue, 
    binaryObject POISON) 
    {
        this.tooManyErrors = tooManyErrors;
        this.queue = queue;
        this.POISON = POISON;
    }

    @Override
    public void run() 
    {

        try 
        {
            while (true) 
            {
                binaryObject take = queue.take();
                process(take);

                // if this is a poison pill, break, exit
                if (take == POISON) 
                {
                    break;
                }
            }
        } 
        catch (InterruptedException e) 
        {
            Thread.currentThread().interrupt();
        }

    }

    private void process(Integer take) throws InterruptedException 
    {
        //this is where all the logic goes that takes the binaryObject
        //grabs all the fields from it such as binaryObject.getFileName,
        //happens. It then outputs a text file with all the fields grabbed
        //from the object. If the producer thread found too many errors
        // I want the extension changed to .err instead of .txt
        // I dont know how to do that

       if(tooManyErrors == false)
       {
           extension = ".txt";

           createFile(extension);
       }
       else
       {
           extension = ".err";

           createFile(extension);
       }
    }

   private void createFile(String extension)
   {
     //...
   }

}
1
since the Consumer does not have a reference to the Producer - i don't think there is a way. Either your Main class has the tooManyErrors field, or both the consumer/rproducer have it. there's no need to have it everywhere, as that will only result in shadowing. - Shark
I see what would I have to do to be able to see that value from the consume thread? pass in a reference to the producer thread as in new Thread(new Consumer(Producer, tooManyErrors, queue, poison)).start(); - greenway

1 Answers

0
votes

OK, a couple of things here. I assume there is only one producer and one consumer. If that is the case you can mark the classes producer and consumer as static. If you mark the class as static, only one instance of its fields will exist - it will be a singleton. You can mark any of the fields of the producer and consumer as not private, and the simply access the fields. somethingINeed = Producer.fieldThatIsNotPrivate from inside the consumer and vice versa.

Another alternative would be to keep a handle to the object you want and pass that into the constructor.

Producer p = new Producer(tooManyErrors, queue, poison);
Consumer c = new Consumer(tooManyErrors, queue, poison);
p.setConsumer(c);
c.setProducer(p);
new Thread(p).start();
new Thread(c).start();

And you could create accessors for whatever fields need to share information.