Q:

Run two threads until the share array is empty java

I'm working for a homework that use 2 threads and need to get a solution for the consumer-productor problem in java using threads and synchronized blocks. I think I already have everything correct just need to keep both of my thread running until the arrayList is empty. The code right now have two different classes one for the consumer and one for the productor.
They both have two share variables one is a boolean to check when the buffer is empty or not and the other is an array list that is the buffer when one thread reads from it and the other writes to it.
I use another arrayList (chars) so the string that is passed to the productor thread can be change to an arrayList and manipulate easier the next character that the productor thread will write in.
My question is how to run both threads until the shared arrayList "chars" is empty
public class ProductorConsumidor {

/**
 * @param args the command line arguments
 */

static ArrayList <String> buffer = new ArrayList();
static Object sync = new Object();
static Boolean isEmpty = true;
static ArrayList <String> chars = new ArrayList();

private static class productorT extends Thread {
    String phraseS;
    String[] phraseArray;

    public productorT (String phrase) {
        this.phraseS = phrase;
        this.phraseArray = phraseS.split("");
        for (String e : phraseArray) {
            chars.add(e);
        }
    }

    public void run () {
        synchronized (sync) {
            if (isEmpty) {
                for (int i = 0; i < 3; i++) {
                    buffer.add(chars.get(i));
                    System.out.println("Writing " + buffer.get(i));
                }
                chars.remove(0);
                chars.remove(0);
                chars.remove(0);
                System.out.println(chars);
                System.out.println("Producer thread going to sleep now");
                isEmpty = false;
            }
        }
    }
}

private static class consumerT extends Thread {

   public void run () {
       synchronized(sync) {
            if (!isEmpty) {
                for (int i = 0; i < 3; i++) {
                    System.out.println("Reading " + buffer.get(i) + " ");
                }
                buffer.clear();
                System.out.println("Consumer thread going to sleep now");
                isEmpty = true;
            }
        }  
   }
}

public static void main(String[] args) {
    Thread productorT = new productorT("This text is to small to explain a concept cleary");
    Thread consumerT = new consumerT();

    productorT.start();
    consumerT.start();

    try {
        productorT.join();
        consumerT.join();
    } catch (InterruptedException e) {
        System.out.println(e);
    }
}

}

A:

To run until chars is empty requires more explicit synchronization between the producer and consumer.  While you used synchronized blocks, there was not wait()/notify() logic.  The following is working code that drains all the characters (based on a CAPACITY) and demonstrates one way of giving time to the producer to write some characters, then switching back to the consumer to read, and so on, until processing is complete (isEmpty is true).  This effectively demonstrates how to share an array between two processes.
public class ProductorConsumidor {

/**
 * @param args
 *            the command line arguments
 */

static ArrayList<String> buffer = new ArrayList<>();
static Object sync = new Object();
static volatile Boolean isEmpty = false;
static ArrayList<String> chars = new ArrayList<>();

final static int CAPACITY = 3;

private static class productorT extends Thread {
    String phraseS;
    String[] phraseArray;

    public productorT(String phrase) {
        this.phraseS = phrase;
        this.phraseArray = phraseS.split("");
        for (String e : phraseArray) {
            chars.add(e);
        }
    }

    public void run() {
        boolean isStopped = false;
        while (!isEmpty && !isStopped) {
            for (int i = 0; i < CAPACITY && !chars.isEmpty(); i++) {
                System.out.println("> Writing " + chars.get(0));
                buffer.add(chars.get(0));
                chars.remove(0);
            }
            isEmpty = chars.isEmpty();
            synchronized (sync) {
                sync.notifyAll();
                try {
                    sync.wait();
                } catch (InterruptedException e) {
                    System.out.println("Producer has been requested to stop");
                    isStopped = true;
                }
            }
            System.out.println("Remaining="+chars);
        }
        System.out.println("Producer thread has completed writing data");
    }
}

private static class consumerT extends Thread {

    public void run() {
        synchronized (sync) {
            boolean isStopped = false;
            while (!isEmpty && !isStopped) {
                try {
                    System.out.println("Consumer is waiting for more data");
                    sync.wait();
                    for (int i = 0; i < buffer.size(); i++) {
                        System.out.println("< Reading " + buffer.get(i) + " ");
                    }
                    buffer.clear();
                    System.out.println("Consumer thread has completed reading data");
                    sync.notify();
                } catch (InterruptedException e) {
                    System.out.println("The thread has been request to stop");
                    isStopped = true;
                }
            }
        }
    }
}

public static void main(String[] args) {
    final String messageToWrite = "This text is to small to explain a concept clearly";
    Thread productorT = new productorT(messageToWrite);
    Thread consumerT = new consumerT();

    consumerT.start();
    productorT.start();

    try {
        productorT.join();
        consumerT.join();
    } catch (InterruptedException e) {
        System.out.println(e);
    }
}

}

