Over a million developers have joined DZone.

Clojure, Java, and Sleeping Barbers

· Java Zone

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

My friend Matt wrote a pretty rad (yep, I used that word) implementation of the “sleeping barber” program in Clojure. Matt’s code had an almost sentence-like simplicity to it – pithy but expressive — that impressed me.

As we walked through how the program worked, I couldn’t help but wonder about a Java implementation. So, I wrote one.

package name.christianson.mike;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.*;

public class BarberShop {
	public static final int NUM_WAITING_ROOM_CHAIRS = 3;
	public static final long SHOP_RUNTIME_MILLIS = SECONDS.toMillis(10);
	private final static AtomicBoolean shopOpen = new AtomicBoolean();
	private final static AtomicInteger totalHaircuts = new AtomicInteger();
	private final static AtomicInteger lostCustomers = new AtomicInteger();
	private final BlockingQueue<Object> waitingRoom = new LinkedBlockingQueue<>(NUM_WAITING_ROOM_CHAIRS);

	public static void main(String[] args) throws InterruptedException {
		BarberShop shop = new BarberShop();

		ExecutorService executor = Executors.newFixedThreadPool(3);
		
		Runnable customerGenerator = new CustomerGenerator(shop);
		Runnable barber = new Barber(shop);
		Runnable progressTracker = new ProgressTracker(shop);
		
		shop.open();
		
		executor.execute(progressTracker);
		executor.execute(barber);
		executor.execute(customerGenerator);
		executor.shutdown();
		
		Thread.sleep(SHOP_RUNTIME_MILLIS);
		
		shop.close();
	}

	private void close() {
		shopOpen.set(false);
	}

	private void open() {
		shopOpen.set(true);
	}

	public boolean isOpen() {
		return shopOpen.get();
	}

	public boolean seatCustomerInWaitingRoom(Object customer) {
		boolean customerSeated = waitingRoom.offer(customer);
		if(!customerSeated) {
			lostCustomers.incrementAndGet();
		}
		return customerSeated;
	}
	
	public Object napUntilCustomerArrives() throws InterruptedException {
		return waitingRoom.take();
	}

	public void recordHaircut() {
		totalHaircuts.incrementAndGet();
	}

	public Object lostCustomers() {
		return lostCustomers.get();
	}

	public Object haircuts() {
		return totalHaircuts.get();
	}

}


Matt’s is self-contained in one file while mine is split across multiple files. (I opted to not use anonymous/inner classes.) I think we were a bit surprised that the concurrency logic is similar and the ”main” driving section of each implementation follows the same set-up procedure. Perhaps my code was influenced by way of transliteration?

I’m struck by how Matt’s Clojure programs reads somewhat like a story while my Java program reads like instructions for a computer.

 

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!

Topics:

Published at DZone with permission of Mike Christianson , DZone MVB .

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}