5 Python solutions using threading (Barrier, Condition, Event, Lock, Semaphore) with explanation
Use a for loop to print n times from zero thread, n//2 times from even, and (n+1)//2 times from odd to form 2*n-digit output. \n\nUse two two-thread barriers fo
Use a for loop to print `n` times from `zero` thread, `n//2` times from `even`, and `(n+1)//2` times from `odd` to form `2*n`-digit output. \n\nUse two two-thread barriers for the even / odd threads and a lock for the zero thread. Each time zero thread prints, it increments `self.ct` and determines whether to run `even` or `odd` thread next. Each time `even` or `odd` prints, it unlocks the zero thread. \n\n```\nfrom threading import Barrier, Lock\n\nclass ZeroEvenOdd:\n    def __init__(self, n):\n        self.n = n\n        self.ct = 0\n        self.barriers = [Barrier(2),Barrier(2)]\n        self.zero_lock = Lock()\n\n    def zero(self, printNumber):\n        for _ in range(self.n):\n            self.zero_lock.acquire()\n            printNumber(0)\n            self.ct += 1\n            self.barriers[self.ct % 2].wait()\n        \n    def even(self, printNumber):\n        for _ in range(self.n//2):\n            self.barriers[0].wait()\n            printNumber(self.ct)\n            self.zero_lock.release()\n        \n    def odd(self, printNumber):\n        for _ in range((self.n+1)//2):\n            self.barriers[1].wait()\n            printNumber(self.ct)\n            self.zero_lock.release()\n```\n\nUse three 0-value Semaphores to lock and unlock each thread.\n\n```\nfrom threading import Semaphore\n\nclass ZeroEvenOdd:\n    def __init__(self, n):\n        self.n = n\n        self.ct = 0\n        self.gates = [Semaphore(),Semaphore(),Semaphore()]\n        self.gates[0].acquire()\n        self.gates[1].acquire()\n        \n     def zero(self, printNumber):\n        for _ in range(self.n):\n            self.gates[2].acquire()\n            printNumber(0)\n            self.ct += 1\n            self.gates[self.ct % 2].release()\n        \n    def even(self, printNumber):\n        for _ in range(self.n//2):\n            self.gates[0].acquire()\n            printNumber(self.ct)\n            self.gates[2].release()\n        \n    def odd(self, printNumber):\n        for _ in range((self.n+1)//2):\n            self.gates[1].acquire()\n            printNumber(self.ct)\n            self.gates[2].release()\n```\n\nThe same can be done using Lock\n\n```\nfrom threading import Lock\n\nclass ZeroEvenOdd:\n    def __init__(self, n):\n        self.n = n\n        self.ct = 0\n        self.gates = [Lock(),Lock(),Lock()]\n        self.gates[0].acquire()\n        self.gates[1].acquire()\n        \n    def zero(self, printNumber):\n        for _ in range(self.n):\n            self.gates[2].acquire()\n            printNumber(0)\n            self.ct += 1\n            self.gates[self.ct % 2].release()\n        \n    def even(self, printNumber):\n        for _ in range(self.n//2):\n            self.gates[0].acquire()\n            printNumber(self.ct)\n            self.gates[2].release()\n        \n    def odd(self, printNumber):\n        for _ in range((self.n+1)//2):\n            self.gates[1].acquire()\n            printNumber(self.ct)\n            self.gates[2].release()\n```\n\nEvent works in a similar way but with `set`, `wait` and `clear` instead of `release`, `acquire`\n\n```\nfrom threading import Event\n\nclass ZeroEvenOdd:\n    def __init__(self, n):\n        self.n = n\n        self.ct = 0\n        self.print = [Event(),Event(),Event()]\n        self.print[2].set()\n        \n    def zero(self, printNumber):\n        for _ in range(self.n):\n            self.print[2].wait()\n            self.print[2].clear()\n            printNumber(0)\n            self.ct += 1\n            self.print[self.ct % 2].set()\n        \n    def even(self, printNumber):\n        for _ in range(self.n//2):\n            self.print[0].wait()\n            self.print[0].clear()\n            printNumber(self.ct)\n            self.print[2].set()\n        \n    def odd(self, printNumber):\n        for _ in range((self.n+1)//2):\n            self.print[1].wait()\n            self.print[1].clear()\n            printNumber(self.ct)\n            self.print[2].set()\n```\n\nUse Condition to track the next thread to run in a shared variable\n\n```\nfrom threading import Condition\n\nclass ZeroEvenOdd:\n    def __init__(self, n):\n        self.n = n\n        self.ct = 0\n        self.condition = Condition()\n        self.order = 2\n        \n    def zero(self, printNumber):\n        for _ in range(self.n):\n            with self.condition:\n                self.condition.wait_for(lambda: self.order == 2)\n                printNumber(0)\n                self.ct += 1\n                self.order = self.ct % 2\n                self.condition.notify(2)\n        \n    def even(self, printNumber):\n        for _ in range(self.n//2):\n            with self.condition:\n                self.condition.wait_for(lambda: self.order == 0)\n                printNumber(self.ct)\n                self.order = 2\n                self.condition.notify(2)\n        \n    def odd(self, printNumber):\n        for _ in range((self.n+1)//2):\n            with self.condition:\n                self.condition.wait_for(lambda: self.order == 1)\n                printNumber(self.ct)\n                self.order = 2\n                self.condition.notify(2)\n```
[]