I finally got around to do this.
I ended up changing the game state into a map. I left it as an atom as suggested.
For client polling I defined a thread pool
(def ^ExecutorService poller-thread-pool (Executors/newCachedThreadPool))
And as Clojure functions are Runnable I just call the function that requests action from client and then updates the game state for that particular player.
(let [player-keys-and-states (seq game-state)
threads (map request-and-update player-keys-and-states)
tasks (map #(.submit poller-thread-pool %) threads)]
(dorun tasks))
Dorun is required to start each thread/function since map returns a lazy sequence.
Edit
I had to change this so that only one thread updates the values in game-state since it ended up in strange states when those poller threads changed it too. I added a new map for the pollers to update and then have the "main" thread read for that map and update the game-state with the values there.