Desafio do Papai Noel (Santa Claus problem)

Olá meus caros amigos. Estou com um desafio muito interessante dado pelo meu professor em uma cadeira de processamento paralelo na faculdade. Segundo ele, é um problema bastante conhecido no meio da computação quando se fala no uso de threads e multiprocessamento. O enunciado é o seguinte:

“O problema é
basicamente composto por dez elfos, nove renas e pelo Papai Noel. A vida do Papai
Noel se resume a dormir até que ele seja acordado pelas nove renas ou por um grupo
de três elfos. Caso seja acordado pelas renas, o Papai Noel as amarra ao trenó, distribui
brinquedos, desamarra-as e volta a dormir. Caso seja acordado pelos elfos, o Papai
Noel discute projetos de brinquedos e volta a dormir. A vida de uma rena se resume a
entregar presentes e tirar férias até o próximo ano. A vida de um elfo se resume a
fabricar brinquedos e se reunir com o Papai Noel. Papai Noel dorme em sua casa no
Polo Norte, e só pode ser despertado por 9 renas (todas), quando estão voltando de
suas férias de alguma ilha tropical, ou por alguns elfos, que estão tendo dificuldades em
fazer os brinquedos. O problema de um elfo nunca é grave o suficiente para acordar o
Papai Noel (caso contrário, ele pode nunca conseguir dormir), então, os elfos visitam o
Papai Noel em um grupo de três. Quando os três elfos tem seus problemas resolvidos,
qualquer outro elfo que deseje visitar o Papai Noel deve esperar que os elfos retornem.
Se o Papai Noel acordar e encontrar três elfos esperando na porta de sua loja, junto
com a última rena voltando dos trópicos, o Papai Noel decide que os elfos podem
esperar até depois do Natal, porque é mais importante preparar seu trenó assim que
que possível. Supõe-se que as renas não querem deixar as férias, portanto, ficam lá até
o último momento possível.”

Segue o código de uma solução que alcancei:

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;

public class PapaiNoel {

    private static Random numeroAleatorio = new Random();

    private final Semaphore atencaoDoPapaiNoel;
    private final Semaphore filaDeElfos;
    private final CyclicBarrier tresElfos;
    private final CyclicBarrier noveRenas;
    private final CyclicBarrier treno;
    private final CyclicBarrier problemaResolvido;

    private final static int numeroDeRenas = 9;
    private final static int numeroDeElfos = 10;
    private final static int elfosParaAcordar = 3;
    private final static int ultimaRena = 0;
    private final static int terceiroElfo = 0;

    private PapaiNoel () {
        atencaoDoPapaiNoel = new Semaphore(1, true);
        filaDeElfos = new Semaphore(elfosParaAcordar, true);
        tresElfos = new CyclicBarrier(elfosParaAcordar, new Mensagem("> " + elfosParaAcordar + " elfos precisam de ajuda!"));
        noveRenas = new CyclicBarrier(numeroDeRenas, () -> System.out.println(">>> Todas as renas em casa para a entrega!!"));
        treno = new CyclicBarrier(numeroDeRenas, new Amarracao());
        problemaResolvido = new CyclicBarrier(elfosParaAcordar, new Mensagem("> Elfos de volta ao trabalho..."));

        ArrayList<Thread> threads = new ArrayList<>();

        for (int i = 0; i < numeroDeElfos; i++) {
            threads.add(new Thread(new Elfo(i)));
        }

        for (int i = 0; i < numeroDeRenas; i++) {
            threads.add(new Thread(new Rena(i)));
        }

        System.out.println("<--------- INÍCIO --------->");
        for (Thread t : threads) {
            t.start();
        }
    }

    static class Mensagem implements Runnable {
        String texto;
        Mensagem(String texto) {
            this.texto = texto;
        }
        @Override
        public void run() {
            System.out.println(texto);
        }
    }

    static class Amarracao implements Runnable {
        boolean trenoPreparado;
        Amarracao() {
            trenoPreparado = false;
        }
        @Override
        public void run() {
            trenoPreparado = !trenoPreparado;
            if (trenoPreparado) {
                System.out.println(">>> Renas amarradas e prontas!");
            } else {
                System.out.println(">>> Entrega realizada, renas desamarradas.\n    Férias das renas iniciadas!");
            }
        }
    }

    class Elfo implements Runnable {

        int id;

        Elfo(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(numeroAleatorio.nextInt(3000));

                while (true) {
                    filaDeElfos.acquire();
                    System.out.println("> Elfo " + id + " está com problemas...");

                    int elfo = tresElfos.await();
                    if (elfo == terceiroElfo) {
                        System.out.println("> Papai Noel está ajudando os elfos...");
                        atencaoDoPapaiNoel.acquire();
                    }

                    Thread.sleep(numeroAleatorio.nextInt(1000));
                    System.out.println("> Problema do elfo " + id + " resolvido!");
                    problemaResolvido.await();

                    if (elfo == terceiroElfo) {
                        atencaoDoPapaiNoel.release();
                    }
                    filaDeElfos.release();

                    Thread.sleep(numeroAleatorio.nextInt(2500));
                }
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    class Rena implements Runnable {

        int id;

        Rena(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(4000 + numeroAleatorio.nextInt(500));
                    System.out.println("> Rena " + id + " voltou de suas férias no Caribe.");

                    int rena = noveRenas.await();
                    if (rena == ultimaRena) {
                        atencaoDoPapaiNoel.acquire();
                        System.out.println(">>> Entrega de Natal iniciando...");
                    }

                    treno.await();
                    Thread.sleep(numeroAleatorio.nextInt(150));

                    rena = treno.await();
                    if (rena == ultimaRena) {
                        atencaoDoPapaiNoel.release();
                        System.out.println(">>> Todos os brinquedos foram entregues!!!");
                    }
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        new PapaiNoel();
    }

}

Na última aula, meu professor comentou que seriam necessárias vinte threads no mínimo. Essa solução utiliza dezenove (dez elfos e nove renas). Como posso fazer com que a solução tenha o Papai Noel como mais uma thread? Eu gostaria que fosse assim para ter um maior controle da prioridade das renas sobre os elfos, e talvez colocar um tempo que o Papai Noel levaria para acordar e ir até a porta de sua loja (?). Se eu colocar o Papai Noel como Runnable também, o que ele vai fazer? O que vai ter nessa thread? Tô meio perdido hahaha