Synchronization of two method which work on ArrayList

Viewed 79

I have Sum class, Creator class, Item class and Main. The Creator creates random Items and adds them to an ArrayList which is in the Item class. The Sum class reads items and sums the weight of all. In the Main class I start in multiple threads Creator and Sum. Both classes implement Runnable and override the run method. After 200 created are items into is printed in the console.

How to synchronize this methods? When I start threads, the method from Sum ends first and returns weight 0 and after that Creator creating 40 000 random Items. I will create items and at the same time Sum all weights of them and in the end Return how many items were created and weight of all of them.

Sum class method:

@Override
    public synchronized void run() {

        for(Towar x: Towar.list){
            try {
                Thread.currentThread().wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter++;
            sum+=x.getWaga();
            if(counter%100==0){
                System.out.println("Sum of "+counter+" items");
            }
        }
        System.out.println("Total weight of Items: "+sum);
    }

Creator class method:

@Override
    public void run() {
        reader=new Scanner(text);
        while(reader.hasNextLine()){
            counter++;
            String[] x=reader.nextLine().split("_");
            synchronized (Towar.getList()){
                Towar.add(new Towar(x[0], Integer.parseInt(x[1])));
                Towar.list.notify();
                if(counter%200==0){
                    System.out.println("Created "+counter+" items");
                }
            }

        }
        System.out.println("Created in total: "+counter+" items");
    }
2 Answers

BlockingQueue

I would recommend using implementations of the BlockingQueue interface, instead of ArrayList. A BlockingQueue is thread-safe.

To quote the Javadoc:

BlockingQueue implementations are thread-safe. All queuing methods achieve their effects atomically using internal locks or other forms of concurrency control. However, the bulk Collection operations addAll, containsAll, retainAll and removeAll are not necessarily performed atomically unless specified otherwise in an implementation. So it is possible, for example, for addAll(c) to fail (throwing an exception) after adding only some of the elements in c.

Example code

 class Producer implements Runnable {
   private final BlockingQueue queue;
   Producer(BlockingQueue q) { queue = q; }
   public void run() {
        reader=new Scanner(text);
        while(reader.hasNextLine()){
            counter++;
            String[] x=reader.nextLine().split("_");
            q.put(new Towar(x[0], Integer.parseInt(x[1])));
            if(counter%200==0){
              System.out.println("Created "+counter+" items");
            }
        }
        q.put(null);
        System.out.println("Created in total: "+counter+" items");
   }
 }

 class Consumer implements Runnable {
   private final BlockingQueue queue;
   Consumer(BlockingQueue q) { queue = q; }
   public void run() {
     long sum = 0;
     try {
       while (true) { 
          Towar x = (Towar)queue.take();
          if (x == null) return;
          counter++;
          sum+=x.getWaga();
          if(counter%100==0){
                System.out.println("Sum of "+counter+" items");
          }
       }
     } catch (InterruptedException ex) { ... handle ...}
   }
 }

CountDownLatch You can do it in multiple ways. One way of doing it using CountDownLatch to wait on the number of items provided to be created and pass a Signal for Sum Thread saying creation is done. List will throw Concurrent Modification while 1 thread is adding objects and another one is using Enhanced for loop. You can get away with this problem either by using a Simple for loop or while loop etc.

I have written a Sample Program that can be used for your requirement. Go thru it 

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class SynchronousListItems {

    public static void main(String[] args) {

        List<Item> items = new ArrayList<>();

        CountDownLatch creatorLatch = new CountDownLatch(1);
        CountDownLatch sumLatch = new CountDownLatch(1);

        int numberOfItemsToCreate = 100;

        Thread createrThread = new Thread(new Creator(items, creatorLatch, numberOfItemsToCreate));
        createrThread.setDaemon(true);

        Sum sumObj = new Sum(items, sumLatch);

        Thread sumThread = new Thread(sumObj);
        sumThread.setDaemon(true);

        createrThread.start();
        sumThread.start();

        try {
            creatorLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            sumObj.setStopSignal(true);
        }

        try {
            sumLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class Creator implements Runnable {

    private List<Item> items;

    private CountDownLatch creatorLatch;

    private int numberOfItemsToCreate;

    public Creator(List<Item> items, CountDownLatch creatorLatch, int numberOfItemsToCreate) {
        this.items = items;
        this.creatorLatch = creatorLatch;
        this.numberOfItemsToCreate = numberOfItemsToCreate;
    }

    @Override
    public void run() {

        Random random = new Random(10L);

        while (numberOfItemsToCreate > 0) {
            int wage = random.nextInt();
            System.out.println(wage);
            items.add(new Item(wage));
            numberOfItemsToCreate--;
        }

        creatorLatch.countDown();
    }

}

class Item {

    private int wage;

    public Item(int wage) {
        this.wage = wage;
    }

    public int getWage() {
        return wage;
    }

}

class Sum implements Runnable {
    private boolean stopSignal;

    private List<Item> items;

    private int total;

    private int itemsCounter;

    private CountDownLatch sumLatch;

    public Sum(List<Item> items, CountDownLatch sumLatch) {
        this.items = items;
        this.sumLatch = sumLatch;
    }

    @Override
    public void run() {

        try {
            while (!stopSignal) {
                if (items != null && !items.isEmpty() && items.size() > itemsCounter) {
                    System.out.println("Itemes are not empty");
                    System.out.println("Itemes Counter " + itemsCounter);
                    while (itemsCounter < items.size()) {
                        Item item = items.get(itemsCounter);
                        System.out.println("Item Pulled");
                        total = total + item.getWage();
                        itemsCounter++;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("Final Items Counter :" + itemsCounter);
            System.out.println("Total :" + total);
            sumLatch.countDown();
        }
    }

    public void setStopSignal(boolean stopSignal) {
        this.stopSignal = stopSignal;
    }
}
Related