目次
Scheduler Tutorial
このチュートリアルでは、Bukkitのスケジューラの利用方法を説明しています。
主な注意事項
主な注意事項は次の2点です。
- 他のスレッドからAPIのメソッドを(一部例外あり)を呼び出さない
- メインのスレッドをスリープ状態にしてはならない
Tutorial
The scheduler gives the following options;
Three types of task
- Delayed Tasks
- Repeating Tasks
- Tasks which return a value
These tasks can be exeuted by two types of thread
- Main server thread
- Dedicated thread
Thread Model
The most important question when using the scheduler is deciding which thread types to use. Using the wrong thread type can cause intermittent problems that can be very hard to replicate. These problems may only occur on heavily used servers and not on a test server.
While the minecraft server is multi-threaded, there is one main server thread and other helper threads.
Many data structures are only accessed by the server from the main thread. If you attempt to modify any of these from another thread, this may cause data corruption since two threads could be accessing the data at the same time.
The main thread handles the server clock tick system. It executes 20 times a second and then sleeps for the rest of the clock tick.
Very few Bukkit API calls are thread-safe. Unless you have checked that the API call you are making is thread-safe, it is best to assume that it isn't.
Synchronous Tasks
These are tasks that are executed by the main server thread.
Remember: If this thread stops, the whole server will freeze.
The main thread CAN be used for
- Scheduler method calls
- Care needs to be taken when dealing with Future objects returned by the Scheduler (see below)
- Other tasks which don't block
The main thread MUST be used for
- Tasks which call Bukkit API methods (except thread safe method, see below)
The main thread MUST NOT be used for
- Tasks which put the current thread to sleep
- This includes tasks which block, i.e. network read tasks
It is OK to use synchronize on the main thread, but only if you are sure that it will block for almost zero time.
Asynchronous Tasks
These tasks are executed by a thread other than the main server thread. These threads can be put to sleep/blocked without causing any problems as the thread is dedicated to the task. The rules for these threads are the opposite of the rules for synchronous threads.
An async thread CAN be used for
- Scheduler method calls
- Other tasks which don't access the plugin API
An async thread MUST be used for
- Tasks which put the current thread to sleep
- This includes tasks which block, i.e. network read tasks
An async thread MUST NOT be used for
- Tasks which call Bukkit API methods (except thread safe method, see below)
Task Types
There are 3 types of task, repeating tasks, delayed tasks, and tasks which return a value.
The API interface is described [here]
Delayed Tasks
Tasks can be submitted to be executed after a delay. This delay can be set to zero.
Example
This submits a task to the main thread to be executed after 3 seconds.
myPlugin.getServer().getScheduler().scheduleSyncDelayedTask(myPlugin, new Runnable() { public void run() { getServer().broadcastMessage("This message is broadcast by the main thread"); } }, 60L);
The time delay is 60. This counts in server ticks. There are 20 ticks per second, so the delay is 60/20 = 3.
This code could be executed by another thread. If the code
getServer().broadcastMessage("This message is broadcast by an async thread");
was executed by another thread, it could cause problems since the broadcastMessage method is being executed by another thread.
Repeating Tasks
Tasks can be submitted to be repeated. An initial delay is also set.
Example
This submits a task to an async thread to be executed after 3 seconds with a period of 10 seconds.
myPlugin.getServer().getScheduler().scheduleAsyncRepeatingTask(myPlugin, new Runnable() { public void run() { System.out.println("This message is printed by an async thread"); } }, 60L, 200L);
Both times count in server ticks. The initial delay is 60/20 = 3 seconds and the period is 200/20 = 10 seconds.
The above code will print "This message is printed by an async thread" every 10 seconds.
Tasks which return a value
It is possible to submit tasks which return a value to the scheduler. Only submission for execution by the main thread is supported.
The purpose of this system is to allow API methods that return values to be called by other threads.
The scheduler returns a Future object that can be used to obtain the result.
Example
Future<String> returnFuture = myPlugin.getServer().getScheduler().callSyncMethod(myPlugin, new Callable<String>() { public String call() { return "This is the string to return"; } }); try { // This will block the current thread String returnValue = returnFuture.get(); System.out.println(returnValue); } catch (InterruptedException e) { System.out.println("Interrupt triggered which waiting on callable to return"); } catch (ExecutionException e) { System.out.println("Callable task threw an exception"); ee.getCause().printStackTrace(); }
This will submit the callable and then use the Future.get() method. This method sleeps the current thread until the Callable has returned a value and gets the result.
Use From The Main Thread
This is NOT recommended.
This system isn't intended to be used from the main thread.
If you use the .get() method on the future, it sleeps the current thread (i.e. the main thread), until the (asleep) main thread completes the task.
If you absolutely must use it in the main thread, you MUST check the .isDone() method before calling the .get() method of Future objects returned by the scheduler. Otherwise, the main thread will sleep waiting for the task to be completed (by the main thread).
Thread Safe API Methods
Bukkit API methods which are thread safe are:
- All the scheduler methods
- Bukkit.getServer()
- Server.getBukkitVersion()
- World.getUID()
- World.getMaxHeight()
- World.getSeed()
- World.getBlockTypeIdAt(int x, int y, int z)
- Entity.getEntityId()
- Entity.getUniqueId()
- This list is not complete.