Go uses channels to create concurrency. If the channel is full then you must wait. If the channel is empty and you want something out of it you must wait.
Java uses lock, synch ad the wait set. The wait set is wait(), waitAll() and notifyAll(). You have a queue with the ability to deposit, fetch and keep track of whether the queue is full. Fetch and Deposit must be synchronized. The wait ensures that the consumer must wait for there to be something to fetch from the queue to wake up and try to grab it. Notifyall() tells the wait set to wake up and try to perform their threads action.
Go handles all of this notifying and waiting with channels and is much cleaner.
consumer1 calls wait: puts thread in wait since it’s empty
producer1 deposits and wakes up consumer1 so it wakes up buf is 1
consumer consumes and sets fil to 0
producer goes into the synch queue to wait for consumer to finish
Look to pictures::
Last 3 lines are on me.