Implementing logic by threads is almost always a very bad idea. Multi-threaded programming is difficult! You will run into race conditions and deadlocks resulting in impossible to reproduce bugs.
There is usually only one justifiable reason to use multithreading, and that's performance: A program can sometimes be made faster by sharing work between CPU cores by running one thread per available core. But when you create more threads than you have cores, you usually achieve the opposite effect, because creation and management of threads is a quite expensive operation in itself.
Instead of running a separate thread per entity, have the main loop of your gameserver process all of them in order:
for (FakePlayer p: fakePlayers) {
p.update();
}
If you want the object to perform an action every n game-ticks (you should really measure time in game-ticks, not seconds), it should have an internal counter when it performed the last action and check that counter in their update-function.
When you implement this and notice that the performance is too slow and after profiling it you found out that the bottleneck is the CPU, you might consider to parallelize the processing of your game objects into multiple threads. A good tool for this is to use a ThreadPool because it will automatically assign tasks to a number of threads in a smart way without you having to manage threads yourself. Call awaitTermination after passing all your update tasks to it to make sure the procession is finished before you continue with your main loop.
for (FakePlayer p: fakePlayers) {
threadPool.submit(p -> p.update());
}
threadPool.awaitTermination();
And now for what's so hard about this: If you decide to do this and the fakePlayers.update methods read and write any data shared between them, you need to make sure these data structures are "thread-save". To understand all the ramifications of this, you should read and understand the official Java lesson about concurrency. Yes, all of it. Yes, from front to back. Otherwise you will get randomly occuring and impossible to reproduce bugs and your player-community will start a lynch mob.
A save way to do this without having to bash in your head too much about synchronization is to do only the decision finding (which should only require read-access to shared data) in parallel and then do the decision execution in serial order.
for (FakePlayer p: fakePlayers) {
threadPool.submit(p -> p.thinkAboutNextMove());
}
threadPool.awaitTermination();
for (FakePlayer p: fakePlayers) {
p.performNextMove();
}